diff options
Diffstat (limited to 'kbabel/common')
51 files changed, 11574 insertions, 0 deletions
diff --git a/kbabel/common/Makefile.am b/kbabel/common/Makefile.am new file mode 100644 index 00000000..462cc425 --- /dev/null +++ b/kbabel/common/Makefile.am @@ -0,0 +1,67 @@ +## Makefile.am for libkbabelcommon + +# this has all of the subdirectories that make will recurse into. if +# there are none, comment this out +SUBDIRS = libgettext + +pkgincludedir = $(includedir)/kbabel + +# this is the program that gets installed. it's name is used for all +# of the other Makefile.am variables +lib_LTLIBRARIES = libkbabelcommon.la + +# set the include path for X, qt and KDE +INCLUDES = $(all_includes) + +# which sources should be compiled +libkbabelcommon_la_SOURCES = catalog.cpp catalogitem.cpp editcmd.cpp\ + msgfmt.cpp catalogsettings.cpp tagextractor.cpp diff.cpp\ + argextractor.cpp kbmailer.cpp poinfo.cpp\ + regexpextractor.cpp importplugin.cpp \ + exportplugin.cpp \ + kbproject.cpp \ + kbprojectmanager.cpp \ + kbprojectsettings.kcfgc \ + projectsettings.cpp \ + stringdistance.cpp + +libkbabelcommon_la_LIBADD = $(LIB_KIO) libgettext/libgettext.la +libkbabelcommon_la_LDFLAGS = $(all_libraries) -version-info 5:0:2 -no-undefined + +# these are the headers for your project +noinst_HEADERS = resources.h version.h catalogitem_private.h catalog_private.h kbmailer.h stringdistance.h +pkginclude_HEADERS = catalog.h catalogitem.h editcmd.h msgfmt.h catalogsettings.h findoptions.h catalogview.h tagextractor.h poinfo.h\ + regexpextractor.h itempart.h catalogfileplugin.h kbabeldatatool.h kbproject.h kbprojectmanager.h \ + projectsettings.h + + +# let automoc handle all of the meta source files (moc) +METASOURCES = AUTO + +# KConfigXT project settings +kde_kcfg_DATA=kbprojectsettings.kcfg + +# service type for the filters +kde_servicetypes_DATA = kbabelfilter.desktop +EXTRA_DIST = $(kde_servicetypes_DATA) + +KDE_OPTIONS = nofinal + +version.h: $(srcdir)/../VERSION + echo "/* Generated file - DO NOT EDIT */" > version.h + echo "#undef VERSION" >> version.h + echo "#define VERSION \"`cat $(srcdir)/../VERSION`\"" >> version.h + +catalog.lo: version.h + +# updating of project settings for default project +update_DATA = kbabel-projectrename.upd +updatedir = $(kde_datadir)/kconf_update + +api: + mkdir -p API && kdoc -d API -u $$PWD/API -p -lkdecore -lqt-mt -ldcop $(pkginclude_HEADERS) + +distclean-local: + rm -r -f API + rm -f version.h + diff --git a/kbabel/common/argextractor.cpp b/kbabel/common/argextractor.cpp new file mode 100644 index 00000000..4146a5a7 --- /dev/null +++ b/kbabel/common/argextractor.cpp @@ -0,0 +1,70 @@ +/* **************************************************************************** + This file is part of KBabel + + Copyright (C) 2001 by Matthias Kiefer <matthias.kiefer@gmx.de> + 2002 by Stanislav Visnovsky <visnovsky@nenya.ms.mff.cuni.cz> + + based on code of Andrea Rizzi <rizzi@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the 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 "catalogsettings.h" +#include "argextractor.h" + +#include <kdebug.h> +#include <kstaticdeleter.h> +#include <qregexp.h> + +using namespace KBabel; + +ArgExtractor::ArgExtractor(QString string) : RegExpExtractor( string ) +{ +} + +QStringList *ArgExtractor::_argList=0; +KStaticDeleter< QStringList > sdAL; + +QStringList *ArgExtractor::regExpList() +{ + if(!_argList) + { + sdAL.setObject( _argList, new QStringList ); + //Build the default arg list + _argList->append("%[ndioxXucsfeEgGp]"); + _argList->append("%([0-9]+(\\$))?[-+'#0]?[0-9]*(.[0-9]+)?[hlL]?[dioxXucsfeEgGp]"); + _argList->append("%[0-9]+"); + } + + return _argList; +} + +void ArgExtractor::setArgExpressions( QStringList* list ) +{ + sdAL.setObject( _argList, new QStringList ); + for( QStringList::Iterator it = list->begin() ; it != list->end() ; ++it ) + _argList->append(*it); +} diff --git a/kbabel/common/argextractor.h b/kbabel/common/argextractor.h new file mode 100644 index 00000000..31b28c8a --- /dev/null +++ b/kbabel/common/argextractor.h @@ -0,0 +1,83 @@ +/* **************************************************************************** + This file is part of KBabel + + Copyright (C) 2002 by Stanislav Visnovsky + + based on code of Matthias Kiefer <matthias.kiefer@gmx.de> and + Andrea Rizzi <rizzi@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the 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. + +**************************************************************************** */ +#ifndef _ARG_EXTRACTOR_H_ +#define _ARG_EXTRACTOR_H_ + +#include <qptrlist.h> +#include <qstring.h> +#include <qstringlist.h> +#include <qvaluelist.h> +#include <qregexp.h> + +#include "regexpextractor.h" + +namespace KBabel +{ + +/** + * class to extract arguments from a string + * @author Stanislav Visnovsky <visnovsky@kde.org> + * @author Andrea Rizzi <rizzi@kde.org> + */ +class ArgExtractor : public RegExpExtractor +{ + +public: + /** + * Create an argument extractor for "string" + */ + ArgExtractor(QString string=QString::null); + + /** + * Set a new list of tag regular expressions. It also + * deletes the old tags. + * @param list a list of regular expressions + */ + static void setArgExpressions(QStringList* list); + +protected: + /** + * @return the static list of args + */ + virtual QStringList *regExpList(); + +private: + static QStringList *_argList; +}; + +} + + +#endif diff --git a/kbabel/common/catalog.cpp b/kbabel/common/catalog.cpp new file mode 100644 index 00000000..ffd939b7 --- /dev/null +++ b/kbabel/common/catalog.cpp @@ -0,0 +1,3509 @@ +/* **************************************************************************** + This file is part of KBabel + + Copyright (C) 1999-2000 by Matthias Kiefer <matthias.kiefer@gmx.de> + 2001-2005 by Stanislav Visnovsky <visnovsky@kde.org> + Copyright (C) 2006 by Nicolas GOUTTE <goutte@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the 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 <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 "kbprojectsettings.h" + +#include "resources.h" +#include "version.h" +#include "stringdistance.h" + +#include <kmessagebox.h> +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<CatalogItem>::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<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; +} +*/ + +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 <EMAIL@ADDRESS>, 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<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()); + } + +} + +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<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 == 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<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; + 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<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(); +} + +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<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(); +} + +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<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; +} + +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(); +} + +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<PluralFormType>(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<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++; + } + +} + +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; +} + +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; + } + + 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<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; +} + +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<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()); + } + } + } + + } +} + +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<KBABELADD>" + QString(QChar(0x00B6)) + "</KBABELADD>", "\\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<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; +} + +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; + +} + +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<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 ); + } +} + +void Catalog::setObsoleteEntries(QValueList<CatalogItem> entries) +{ + d->_obsoleteEntries=entries; +} + +QValueList<CatalogItem> 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<uint>& 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<CatalogItem>::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; diff --git a/kbabel/common/catalog.h b/kbabel/common/catalog.h new file mode 100644 index 00000000..db47c48b --- /dev/null +++ b/kbabel/common/catalog.h @@ -0,0 +1,698 @@ +/***************************************************************************** + 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., 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. + +**************************************************************************** */ +#ifndef CATALOG_H +#define CATALOG_H + +#include "pluralforms.h" +#include "itempart.h" +#include "msgfmt.h" +#include "poinfo.h" +#include "catalogfileplugin.h" +#include "kbproject.h" + +#include <qptrlist.h> +#include <qvaluevector.h> +#include <kdemacros.h> +#include <kstandarddirs.h> + +class KDataTool; +class QString; +class QTextStream; +class KFileInfo; +class QTextCodec; +class QFile; +class KDataTool; +class KURL; + +namespace KBabel +{ + +class CatalogView; +class EditCommand; +class FindOptions; +class CatalogItem; +class IdentitySettings; +class MiscSettings; +class SaveSettings; +class TagSettings; + +class CatalogPrivate; + +/** +* This struct represents a position in a catalog. +* A position is a tuple (index,pluralform,textoffset). +* +* @short Structure, that represents a position in a catalog. +* @author Matthias Kiefer <matthias.kiefer@gmx.de> +* @author Stanislav Visnovsky <visnovsky@kde.org> +*/ +struct DocPosition +{ + DocPosition( ) : offset(0), part(UndefPart), item(0), form(0) { } + uint offset; + + Part part; + uint item; + uint form; +}; + +/** +* This struct represents an entry in diff-algorithm sresults. +* +* @short Structure, that represents a diff result. +*/ +struct DiffEntry +{ + QString msgid; + QString msgstr; +}; + +/** +* This class represents a catalog, saved in a po-file. +* It has the ability to load from and save to a po-file. +* Also it defines all necessary functions to set and get the entries +* +* @short Class, that represents a translation catalog(po-file) +* @author Matthias Kiefer <matthias.kiefer@gmx.de> +*/ +class KDE_EXPORT Catalog : public QObject +{ + Q_OBJECT + +public: + enum DiffResult{DiffOk, DiffNotFound, DiffNeedList}; + + /** + * reads header information from the file and searches for charset + * information. + * @param gettextHeader text containing gettext headers + * + * @return Codec for found charset or 0, if no information has been found + */ + static QTextCodec* codecForFile(QString gettextHeader); + + static PoInfo headerInfo(const CatalogItem headerItem); + + /** + * A constructor for an empty message catalog. + * @param parent parent @ref QObject for this catalog + * @param name unique name for this object + * @param configFile configuration file to read config from + */ + Catalog(QObject* parent=0, const char* name=0, QString projectFile = QString() ); + + /** + * Deprecated. A copy constructor. Do not use since each Catalog registers + * its views and there should be no more than one Catalog for a given file. + */ + Catalog(const Catalog&); + virtual ~Catalog(); + + /** + * Get the message context for a given message. + * + * @param index index of the requested message + * @return context for the given message + */ + QString msgctxt(uint index) const; + + /** + * Get list of texts for a given message in original language. Each entry in the list + * represents a single singular/plural form. + * + * @param index index of the requested message + * @param noNewLines set true if the new lines should be eliminated in result + * (useful for searching etc) + * @return list of singular/plural forms of the original text + */ + QStringList msgid(uint index, const bool noNewlines=false) const; + + /** + * Get list of translated texts for a given message. Each entry in the list + * represents a single singular/plural form. + * + * @param index index of the requested message + * @param noNewLines set true if the new lines should be eliminated in result + * (useful for searching etc) + * @return list of translated singular/plural forms + */ + QStringList msgstr(uint index, const bool noNewlines=false) const; + + /** + * Get the comment for a given message. + * + * @param index index of the requested message + * @return comment for the given message + */ + QString comment(uint index) const; + + /** + * Get the context information for a given message. This works + * for GNU gettext PO files comments only. Finds and returns comment lines, + * which start with #: + * + * @param index index of the requested message + * @return context information found in the message comment + * @deprecated + */ + QString context(uint index) const KDE_DEPRECATED; + + /** + * Get the header for the current file. + * + * @return @ref CatalogItem representing the header + */ + CatalogItem header() const; + + /** + * Get the name and e-mail address of the last translator of the current file. + * + * @return string representation of the name and e-mail address + */ + QString lastTranslator() const; + + /** + * @return The list of obsolete entries. These cannot be changed at + * all and are stored for import/export purposes. + */ + QValueList<CatalogItem> obsoleteEntries() const; + + /** + * @return The index of the item, that has the msgid id. + * If no item is found, -1 is returned. + */ + int indexForMsgid(const QString& id) const; + + /** @return a list of tags in entry #index */ + QStringList tagList(uint index); + + /** @return a list of arguments in entry #index */ + QStringList argList(uint index); + + /** + * @return the list of errors found for a given item index + * @param index index of the item to check + */ + QStringList itemStatus(uint index); + + /** + * @return the list of errors found for a given item index + * @param index index of the item to check + * @param recheck flag, if the item status should be checked now + * @param whatToCheck what checks to do (a list of tools to be used + */ + QStringList itemStatus(uint index, bool recheck, QPtrList<KDataTool> whatToCheck); + + /** + * replaces msgstr in catalog item at index index with msgstr + * returns true, if untranslated status changed + */ + //bool setMsgstr(uint index,QString msgstr); + /** + * replaces comment in catalog item at index index with comment + * returns true, if fuzzy status changed + */ + //bool setComment(uint index,QString comment); + /** + * replaces header with given String + * @return false if the header is not valid + */ + bool setHeader(CatalogItem header); + + /** removes string ", fuzzy" from comment */ + void removeFuzzyStatus(uint index); + + /** sets or unsets fuzzy status of entry index */ + void setFuzzy(uint index, bool on); + + /** + * used for session managment when modified file is + * saved in a temp file + */ + void setCurrentURL(const KURL& url); + /** + * @return URL currently represented by the catalog + */ + KURL currentURL() const; + + /** + * @return package name for the current catalog + */ + QString packageName() const; + /** + * @return directory for the package of the current catalog + */ + QString packageDir() const; + /** + * @return package name and directory for the current catalog + */ + QString package() const; + + /** + * Setup the package for the catalog. It tries to parse the + * package and split it to name and directory + * @param package the name and directory to be set as package + */ + void setPackage(const QString& package); + + /** + * @return encoding for the current catalog + */ + QString encoding() const; + + /** + * opens file url by using KDE's network downlad and calls + * openFile with a local filename + */ + ConversionStatus openURL(const KURL& url, const QString& package=QString::null); + + /** + * opens file openURL by using KDE's network downlad and calls + * openFile with a local filename + * sets current URL to saveURL + */ + ConversionStatus openURL(const KURL& openURL, const KURL& saveURL, const QString& package=QString::null); + + /** save the file under the old filename */ + ConversionStatus saveFile(); + /** saves the file under a new filename */ + ConversionStatus saveFileAs(const KURL& url,bool overwrite=false); + + /** + * saves the current catalog in a temporary file and + * returns the name and path of the file. + */ + QString saveTempFile(); + + /** + * @return extra data for the catalog as set by the import filter + */ + QStringList catalogExtraData() const; + /** + * @return ID string of the used import filter + */ + QString importPluginID() const; + /** + * @return list of MIME types (separated by comma) this catalog + * can be viewed as. It is set by the used import filter. + */ + QString mimeTypes() const; + /** + * @return the file codec used for the current catalog + */ + QTextCodec* fileCodec() const; + + /** + * Check syntax of the GNU gettext PO file using 'msgfmt'. + * + * @param output the result as returned by msgfmt + * @param clearError should the errors be cleared before running msgfmt + * @return the file codec used for the current catalog + */ + Msgfmt::Status checkSyntax(QString& output, bool clearErrors=true); + + /** + * checks using an external tool. The tool must provide the "validate" command + * with the datatype expected to be CatalogItem *. The MIME type + * is application/x-kbabel-catalogitem. + */ + bool checkUsingTool(KDataTool* tool, bool clearErrors=true); + + /** + * modifies catalog by applying an external tool on all items. The tool must provide the "validate" command + * with the datatype expected to be CatalogItem *. The MIME type + * is application/x-kbabel-catalogitem. + */ + void modifyUsingTool(KDataTool* tool, const QString& command); + + /** + * Returns true, if there is an ongoing activity, such as load/save + */ + bool isActive(); + + /** + * Stop the current activity, for example load/save or validation + */ + void stop(); + + /** closes the file and deletes all entries */ + void clear(); + + /** @return whether the catalog has been modified since the last save */ + bool isModified() const; + /** sets modified flag */ + void setModified(bool flag); + + /** @return whether the catalog is read-only */ + bool isReadOnly() const; + + /** set if file is generated from a docbook source file (with xml2pot) */ + bool isGeneratedFromDocbook() const; + + /** @return current number of entries */ + uint numberOfEntries() const; + /** @return current number of fuzzies */ + uint numberOfFuzzies() const; + /** @return current number of untranslated entries */ + uint numberOfUntranslated() const; + + /** returns the number of words total, in fuzzy and in untranslated messages */ + void wordCount (uint &total, uint &fuzzy, uint &untranslated) const; + + bool hasFuzzyInFront(uint index) const; + bool hasFuzzyAfterwards(uint index) const; + bool hasUntranslatedInFront(uint index) const; + bool hasUntranslatedAfterwards(uint index) const; + bool hasErrorInFront(uint index) const; + bool hasErrorAfterwards(uint index) const; + bool isFuzzy(uint index) const; + bool isUntranslated(uint index) const; + /** @return whether the entry has an error + * @param index index of the queried entry + * @param pos position of the error (currently only form is filled) + */ + bool hasError(uint index, DocPosition& pos) const; + /** @return type of plural forms for the given entry + * @param index index of the queried entry + */ + PluralFormType pluralForm(uint index) const; + + /** @return type of plural forms this file uses. + */ + PluralFormType pluralFormType() const; + + /** @return number of the plural forms for a given entry + * @param index index of the queried entry + */ + int numberOfPluralForms(uint index) const; + + /** + * returns the expected number of plural forms + */ + int defaultNumberOfPluralForms() const; + + /** + * returns index of next fuzzy entry behind startIndex + * -1 if there is no entry behind. Further, it fills the + * pos parameter with correct index and sets the form to 0. + */ + int nextFuzzy(uint startIndex, DocPosition &pos) const; + /** + * returns index of previous fuzzy entry before startIndex + * -1 if there is no entry before. Further, it fills the + * pos parameter with correct index and sets the form to 0. + */ + int prevFuzzy(uint startIndex, DocPosition &pos) const; + /** + * returns index of next untranslated entry behind startIndex + * -1 if there is no entry behind. Further, it fills the + * pos parameter with correct index and the form. + */ + int nextUntranslated(uint startIndex, DocPosition &pos) const; + /** + * returns index of previous untranslated entry before startIndex + * -1 if there is no entry before. Further, it fills the + * pos parameter with correct index and the form. + */ + int prevUntranslated(uint startIndex, DocPosition &pos) const; + /** + * returns index of next error entry behind startIndex + * -1 if there is no entry behind + */ + int nextError(uint startIndex, DocPosition &pos) const; + /** + * returns index of previous error entry before startIndex + * -1 if there is no entry before + */ + int prevError(uint startIndex, DocPosition &pos) const; + + /** + * returns the header with Information updated, but does _not_ change + * the header in the catalog + */ + CatalogItem updatedHeader(CatalogItem oldHeader, bool usePrefs=true) const; + + /** read settings from the apps config file/project */ + void readPreferences(); + /** save settings to the apps config file/project */ + void savePreferences(); + + void useProject(Project::Ptr project); + Project::Ptr project() const; + + SaveSettings saveSettings() const; + IdentitySettings identitySettings() const; + MiscSettings miscSettings() const; + TagSettings tagSettings() const; + + /** + * reads the header from QTextstream and puts it in header + * I made it static to be able to use this function for + * the search in po-files. This way, I can easily find the first + * catalog entry in the textstream + */ +// static ConversionStatus readHeader(QTextStream& stream,CatalogItem& header); + + + void registerView(CatalogView* view); + void removeView(CatalogView* view); + bool hasView() const; + bool isLastView() const; + + bool isUndoAvailable(); + bool isRedoAvailable(); + /** + * undo the last edit command + * @return the index of the item, which was modified or -1, if no undo available + */ + int undo(); + /** + * redo the last undo command + * @return the index of the item, which was modified or -1, if no redo available + */ + int redo(); + + /** + * finds next occurence of searchstring using opts, starting at pos. + * @param pos starting position and if successful contains position + * of found string. + * @param len contains len of found string + * @ return true, if a string was found. false, if end was reached + */ + bool findNext(const FindOptions* opts, DocPosition& pos, int& len); + /** + * finds previous occurence of searchstring using opts, starting at pos. + * @param pos starting position and if successful contains position + * of found string. + * @param len contains len of found string + * @ return true, if a string was found. false, if begin was reached + */ + bool findPrev(const FindOptions* opts, DocPosition& pos, int& len); + + /** + * Remove the index from the error list + * @param index index to be removed + */ + void removeFromErrorList(uint index); + + /** + * tries to find a corresponding entry for entry entry + * from the list of old messages and calculates the diff for it + */ + DiffResult diff(uint entry, QString* result); + + /** + * sets a list of entries to generate a diff from + */ + void setDiffList( const QValueList<DiffEntry>& ); + + /** + * @return the contents of this catalog as list for diffs + */ + QValueList<DiffEntry> asDiffList(); + + /** + * @return how many plural forms are used in language lang. + * If nothing is found -1 is returned. + */ + static int getNumberOfPluralForms(const QString& lang); + +public slots: + /** + * Update the save setting using this one. + * @param settings the new settings. + */ + void setSettings(KBabel::SaveSettings settings); + /** + * Update the identity setting using this one. + * @param settings the new settings. + */ + void setSettings(KBabel::IdentitySettings settings); + /** + * Update the miscelanous setting using this one. + * @param settings the new settings. + */ + void setSettings(KBabel::MiscSettings settings); + /** + * Update the tag setting using this one. + * @param settings the new settings. + */ + void setSettings(KBabel::TagSettings settings); + + /** + * Apply the given edit command. + * + * @param cmd the command to be applied + * @param souce the view which applies the command + */ + void applyEditCommand(EditCommand* cmd,CatalogView* source); + /** + * Convenience method. Apply a begin edit command for a given message and catalog + * source. + * + * @param index index of to be changed message + * @param part part (msgid,msgstr,comment) of to be changed message + * @param source the view which applies the command + */ + void applyBeginCommand(uint index, Part part, CatalogView* source); + /** + * Convenience method. Apply an end edit command for a given message and catalog + * source. + * + * @param index index of to be changed message + * @param part part (msgid,msgstr,comment) of to be changed message + * @param source the view which applies the command + */ + void applyEndCommand(uint index, Part part, CatalogView* source); + +private slots: + /** slot used for internal long-term activity */ + void stopInternal(); + +private: + /** calls @ref CatalogView::update(EditCommand*) */ + void updateViews(EditCommand* cmd,CatalogView* view2exclude=0); + + /** + * takes over changes, but does not change undo/redo list + */ + void processCommand(EditCommand* cmd, CatalogView* view2exclude=0, bool undo=false); + + /** do the actual file write using plugin */ + ConversionStatus writeFile(QString localfile, bool overwrite=false); + + /** + * generates lists that contain indexes of all fuzzy and untranslated entries + */ + void generateIndexLists(); + + /** + * returns value in list that is lower than parameter index + * or -1 if there is none + */ + int findPrevInList(const QValueList<uint>& list,uint index) const; + /** + * returns value in list that is bigger than parameter index + * or -1 if there is none + */ + int findNextInList(const QValueList<uint>& list,uint index) const; + + /** returns the current date and time in the format of the users choice */ + QString dateTime() const; + + /** clear the list of all errors */ + void clearErrorList(); + + /** + * Tries to find out how many plural forms are used in this language and + * sets numberOfPluralForms accordingly. If nothing is found, + * numberOfPLuralForms is set to -1. + */ + void getNumberOfPluralForms(); + + // this class can fill our contents + friend class CatalogImportPlugin; + + /** set the flag that the catalog is generated from docbook */ + void setGeneratedFromDocbook(const bool generated); + /** set the entries of the catalog */ + void setEntries( QValueVector<CatalogItem> entries); + /** set the obsolete entries of the catalog */ + void setObsoleteEntries( QValueList<CatalogItem> entries); + /** set extra data for the catalog as defined by import plugin */ + void setCatalogExtraData(const QStringList& data); + /** set file codec for the catalog */ + void setFileCodec(QTextCodec* codec); + /** set the list of import errors */ + void setErrorIndex(const QValueList<uint>&errors); + /** set ID of the used import plugin */ + void setImportPluginID(const QString& id); + /** set the MIME types for the current catalog as defined by import plugin */ + void setMimeTypes(const QString& mimeTypes); + +private: + CatalogPrivate* d; + +signals: + void signalError(QString); + void signalResetProgressBar(QString,int); + void signalProgress(int); + void signalClearProgressBar(); + + void signalModified(bool); + /** emitted when the header was changed, maybe when saving */ + void signalHeaderChanged(); + /** emitted when a file was opened or saved under another name */ + void signalFileOpened(bool readOnly); + + void signalNumberOfFuzziesChanged(uint number); + void signalNumberOfUntranslatedChanged(uint number); + void signalTotalNumberChanged(uint number); + + void signalSettingsChanged(KBabel::SaveSettings); + void signalSettingsChanged(KBabel::IdentitySettings); + void signalSettingsChanged(KBabel::MiscSettings); + void signalSettingsChanged(KBabel::TagSettings); + + void signalUndoAvailable(bool); + void signalRedoAvailable(bool); + + /** internal signal sent to external part to stop current ongoing activity*/ + void signalStopActivity(); +}; + +} + +#endif //CATALOG_H diff --git a/kbabel/common/catalog_private.h b/kbabel/common/catalog_private.h new file mode 100644 index 00000000..645353f3 --- /dev/null +++ b/kbabel/common/catalog_private.h @@ -0,0 +1,147 @@ +/* **************************************************************************** + 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., 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. + +**************************************************************************** */ +#ifndef CATALOGPRIVATE_H +#define CATALOGPRIVATE_H + +#include <qintcache.h> +#include <qobject.h> +#include <qptrlist.h> +#include <qstringlist.h> +#include <qmap.h> +#include <qvaluevector.h> +#include <kurl.h> + +#include "msgfmt.h" +#include "catalogsettings.h" +#include "catalogitem.h" +#include "regexpextractor.h" + +class QString; +class QTextStream; +class KFileInfo; +class QTextCodec; +class QFile; +class FindOptions; +class KConfig; + +namespace KBabel +{ + +class CatalogPrivate +{ + +public: + + /** url of the po-file, that belongs to this catalog */ + KURL _url; + QString _packageName; + QString _packageDir; + + /** holds the entries in the catalog */ + QValueVector<CatalogItem> _entries; + /** The header of the po-file. */ + CatalogItem _header; + + /** list of obsolete entries */ + QValueList<CatalogItem> _obsoleteEntries; + + /** identification string for used import filter*/ + QString _importID; + QString _mimeTypes; + + bool _modified; + bool _readOnly; + bool _generatedFromDocbook; + bool _active; + bool _stop; + + QValueList<uint> _fuzzyIndex; + QValueList<uint> _untransIndex; + QValueList<uint> _errorIndex; + + QPtrList<CatalogView> _views; + + TagSettings _tagSettings; + + QPtrList<EditCommand> _undoList; + QPtrList<EditCommand> _redoList; + + QTextCodec *fileCodec; + + QStringList msgidDiffList; + QMap< QString, QStringList > msgstr2MsgidDiffList; + QIntCache<QString> diffCache; + + int numberOfPluralForms; + + Project::Ptr _project; + RegExpExtractor *_tagExtractor, *_argExtractor; + + QStringList _catalogExtra; + + CatalogPrivate(Project::Ptr project) : + _packageName( QString::null ), _packageDir( QString::null ), + _header (project), + _importID( QString::null ), _mimeTypes( "text/plain" ), + _modified(false), _readOnly(false), _generatedFromDocbook(false), + _active(false), _stop(false), + fileCodec(0), diffCache(30,76), numberOfPluralForms(-1), + _project(project) + { + _entries.clear(); + _obsoleteEntries.clear(); + diffCache.setAutoDelete(true); + diffCache.clear(); + + _views.setAutoDelete(false); + + _undoList.setAutoDelete(true); + _redoList.setAutoDelete(true); + _tagExtractor = new RegExpExtractor(QStringList()); + _argExtractor = new RegExpExtractor(QStringList()); + + _catalogExtra.clear(); + } + + ~CatalogPrivate() + { + delete _tagExtractor; + delete _argExtractor; + } +}; + +} + +#endif //CATALOGPRIVATE_H diff --git a/kbabel/common/catalogfileplugin.h b/kbabel/common/catalogfileplugin.h new file mode 100644 index 00000000..f47e0d89 --- /dev/null +++ b/kbabel/common/catalogfileplugin.h @@ -0,0 +1,205 @@ +/* **************************************************************************** + This file is part of KBabel + + Copyright (C) 2002-2003 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., 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. +**************************************************************************** */ +#ifndef IMPORTPLUGIN_H +#define IMPORTPLUGIN_H + +#include <qobject.h> + +#include <kdemacros.h> + +class QString; + +namespace KBabel +{ + +class Catalog; +class CatalogItem; +class CatalogImportPluginPrivate; +class CatalogExportPluginPrivate; + +// ### KDE4: force OK=0 +/** + * Result of the conversion + */ +enum ConversionStatus { + OK, + NOT_IMPLEMENTED, + NO_FILE, + NO_PERMISSIONS, + PARSE_ERROR, + RECOVERED_PARSE_ERROR, + OS_ERROR, + NO_PLUGIN, + UNSUPPORTED_TYPE, + HEADER_ERROR, ///< Old name for a recovered error in header @deprecated + RECOVERED_HEADER_ERROR = HEADER_ERROR, ///< Header error that could be recovered @since 1.11.2 (KDE 3.5.2) + STOPPED, + BUSY, + NO_ENTRY_ERROR ///< The loaded catalog has not any entry! @since 1.11.2 (KDE 3.5.2) +}; + +/** +* This class is the base for import plugins for catalogs. +* It provides "transactional behavior", so the changes are stored in +* catalog only if the import process finishes successfully. +* +* To use it, just subclass and redefine load() and id() methods. +* When importing, you can use the protected methods for setting +* the catalog. New catalog items can be added using appendCatalogItem. +* +* @short Base class for Catalog import plugins +* @author Stanislav Visnovsky <visnovsky@kde.org> +*/ +class KDE_EXPORT CatalogImportPlugin: public QObject +{ + Q_OBJECT + +public: + CatalogImportPlugin(QObject* parent, const char* name); + virtual ~CatalogImportPlugin(); + + /** + * Load the file and fill the corresponding catalog. The file + * is considered to be of @ref mimetype MIME type. + * + * @param file local file name to be opened + * @param mimetype the MIME type is should be handled as + * @param catalog the catalog to be filled + * @return result of the operation + */ + ConversionStatus open(const QString& file, const QString& mimetype, Catalog* catalog); + + /** + * Reimplement this method to load the local file passed as an argument. + * Throughout the run, you can use the protected methods for setting + * the contents of the resulting catalog. + * This method must call \see setMimeTypes to setup correct MIME types + * for the loaded file. Also, it should use \see isStopped to + * abort loading and the signals for providing user feedback. + * @param file file to be loaded + * @param mimetype the expected MIME type (the type used for plugin selection + */ + virtual ConversionStatus load(const QString& file, const QString& mimetype) = 0; + /** + * Reimplement this method to return unique identification of your plugin + */ + virtual const QString id() = 0; + + /** @return the list of all available MIME types for which there + * is a import plugin. + */ + static QStringList availableImportMimeTypes(); + +public slots: + /** stop the current operation */ + void stop(); + +protected: + /** Append a new catalog item, either as normal or as an obsolete one + * @param item the new item + * @param obsolete flag that the item is obsolete + */ + void appendCatalogItem( const CatalogItem& item, const bool obsolete = false ); + + /** set flag that the file is generated from DocBook */ + void setGeneratedFromDocbook(const bool fromDocbook); + /** set the list of parse error indexes */ + void setErrorIndex(const QValueList<uint>& errors); + /** set the file codec */ + void setFileCodec(QTextCodec* codec); + + /** set extra data for the catalog, which can't be stored in + * @ref CatalogItem. The format can be arbitrary */ + void setCatalogExtraData( const QStringList& data ); + /** set the header catalog item */ + void setHeader( const CatalogItem& header ); + /** set the MIME types which can be used for this catalog */ + void setMimeTypes( const QString& catalog ); + + /** start a new transaction. You should never call this method. */ + void startTransaction(); + /** commit the data in the current transaction. You should never call this method. */ + void commitTransaction(); + + /** Flag, whether the operation should be stopped immediately.*/ + bool isStopped() const; + +signals: + /** Signal start of the operation */ + void signalResetProgressBar(QString,int); + /** Signal progress of the operation */ + void signalProgress(int); + /** Signal end of the operation */ + void signalClearProgressBar(); + +private: + CatalogImportPluginPrivate* d; +}; + + +/** +* This class is the base for export plugins for catalogs. +* +* To use it, just subclass and redefine the save() method. +* +* @short Base class for Catalog export plugins +* @author Stanislav Visnovsky <visnovsky@kde.org> +*/ +class KDE_EXPORT CatalogExportPlugin: public QObject +{ + Q_OBJECT + +public: + CatalogExportPlugin(QObject* parent, const char* name); + virtual ~CatalogExportPlugin(); + virtual ConversionStatus save(const QString& file, const QString& mimetype, const Catalog* catalog) = 0; + + static QStringList availableExportMimeTypes(); + +public slots: + void stop(); + +protected: + bool isStopped() const; + +signals: + void signalResetProgressBar(QString,int); + void signalProgress(int); + void signalClearProgressBar(); + +private: + CatalogExportPluginPrivate* d; +}; + +} + +#endif diff --git a/kbabel/common/catalogitem.cpp b/kbabel/common/catalogitem.cpp new file mode 100644 index 00000000..ced02e8c --- /dev/null +++ b/kbabel/common/catalogitem.cpp @@ -0,0 +1,521 @@ +/* **************************************************************************** + This file is part of KBabel + + Copyright (C) 1999-2000 by Matthias Kiefer <matthias.kiefer@gmx.de> + 2002 by Stanislav Visnovsky <visnovsky@nenya.ms.mff.cuni.cz> + Copyright (C) 2006 by Nicolas GOUTTE <goutte@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the 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 "catalogitem.h" +#include "catalogitem_private.h" +#include "editcmd.h" +#include "resources.h" +#include "libgettext/pofiles.h" +#include "libgettext/tokens.h" + +#include "regexpextractor.h" + +#include <qtextstream.h> +#include <qtextcodec.h> +#include <qdom.h> + +using namespace KBabel; + +CatalogItem::CatalogItem(Project::Ptr project) +{ + d=0; + clear(); + d->_project = project; +} + +CatalogItem::CatalogItem(const CatalogItem& item) +{ + d=0; + clear(); + *d=*(item.d); +} + +CatalogItem::~CatalogItem() +{ + delete d; +} + +QString CatalogItem::comment() const +{ + return d->_comment; +} + +QString CatalogItem::msgctxt(const bool noNewlines) const +{ + if( noNewlines ) + { + QString tmp = d->_msgctxt; + tmp.replace("\n", " "); // ### TODO: " " or "" ? + return tmp; + } + else + return d->_msgctxt; +} + +QStringList CatalogItem::msgid(const bool noNewlines) const +{ + QStringList result=d->_msgid; + if( noNewlines ) + { + result.gres( "\n", "" ); + } + return result; +} + +QStringList CatalogItem::msgstr(const bool noNewlines) const +{ + QStringList result=d->_msgstr; + if( noNewlines ) + { + result.gres( "\n", "" ); + } + return result; +} + +QStringList CatalogItem::msgstrAsList(int nr) const +{ + QString str; + if(d->_gettextPluralForm && nr > 0) + { + QStringList::ConstIterator it = d->_msgstr.at(nr); + if(it != d->_msgstr.end()) + { + str = *it; + } + else + { + kdDebug(KBABEL) << "request for non existing plural form " + << nr << endl; + } + } + else + { + str = d->_msgstr.first(); + } + + QStringList list=QStringList::split("\n",str); + + if(str.left(1)=="\n") + list.prepend(""); + + if(list.isEmpty()) + list.append(""); + + return list; +} + +bool CatalogItem::isValid() const +{ + return d->_valid; +} + +void CatalogItem::setMsgctxt(QString msg) +{ + d->_msgctxt=msg; +} + +void CatalogItem::setMsgid(QString msg) +{ + d->_msgid=msg; +} + +void CatalogItem::setMsgid(QStringList msg) +{ + d->_msgid=msg; +} + +void CatalogItem::setMsgstr(QString msg) +{ + d->_msgstr=msg; +} + +void CatalogItem::setMsgstr(QStringList msg) +{ + d->_msgstr=msg; +} + +void CatalogItem::setComment(QString com) +{ + d->_comment=com; +} + +void CatalogItem::setGettextPluralForm( bool _gettextPlural ) +{ + d->_gettextPluralForm = _gettextPlural; +} + +QStringList CatalogItem::errors() const +{ + return d->_errors; +} + +QStringList CatalogItem::tagList( RegExpExtractor& te) +{ + if(!d->_haveTagList) + { + // FIXME: should care about plural forms in msgid + te.setString(msgid(true).first()); + d->_tagList = QStringList(te.matches()); + d->_haveTagList = true; + } + + return d->_tagList; +} + +QStringList CatalogItem::argList( RegExpExtractor& te) +{ + if(!d->_haveArgList) + { + // FIXME: should care about plural forms in msgid + te.setString(msgid(true).first()); + d->_argList = QStringList(te.matches()); + } + + return d->_argList; +} + + +bool CatalogItem::isFuzzy() const +{ + return d->_comment.find( QRegExp(",\\s*fuzzy") ) != -1; +} + +bool CatalogItem::isCformat() const +{ + // Allow "possible-c-format" (from xgettext --debug) or "c-format" + // Note the regexp (?: ) is similar to () but it does not capture (so it is faster) + return d->_comment.find( QRegExp(",\\s*(?:possible-)c-format") ) == -1; +} + +bool CatalogItem::isNoCformat() const +{ + return d->_comment.find( QRegExp(",\\s*no-c-format") ) == -1; +} + +bool CatalogItem::isQtformat() const +{ + return d->_comment.find( QRegExp(",\\s*qt-format") ) == -1; +} + +bool CatalogItem::isNoQtformat() const +{ + return d->_comment.find( QRegExp(",\\s*no-qt-format") ) == -1; +} + +bool CatalogItem::isUntranslated() const +{ + return d->_msgstr.first().isEmpty(); +} + +PluralFormType CatalogItem::pluralForm() const +{ + if( d->_gettextPluralForm ) + return Gettext; + if( d->_msgid.first().startsWith( "_n: " ) ) + return KDESpecific; + else + return NoPluralForm; +} + +int CatalogItem::totalLines() const +{ + int lines=0; + if(!d->_comment.isEmpty()) + { + lines = d->_comment.contains('\n')+1; + } + int msgctxtLines=0; + if(!d->_msgctxt.isEmpty()) + { + msgctxtLines=d->_msgctxt.contains('\n')+1; + } + int msgidLines=0; + QStringList::ConstIterator it; + for(it=d->_msgid.begin(); it != d->_msgid.end(); ++it) + { + msgidLines += (*it).contains('\n')+1; + } + int msgstrLines=0; + for(it=d->_msgstr.begin(); it != d->_msgstr.end(); ++it) + { + msgstrLines += (*it).contains('\n')+1; + } + + if(msgctxtLines>1) + msgctxtLines++; + if(msgidLines>1) + msgidLines++; + if(msgstrLines>1) + msgstrLines++; + + lines+=( msgctxtLines+msgidLines+msgstrLines ); + + return lines; +} + + +void CatalogItem::setSyntaxError(bool on) +{ + if(on && !d->_errors.contains("syntax error")) + d->_errors.append("syntax error"); + else + d->_errors.remove("syntax error"); +} + +QPtrList<EditCommand> CatalogItem::removeFuzzy(bool doIt) +{ + QPtrList<EditCommand> editList; + editList.setAutoDelete(false); + + QString comment=d->_comment; + + if(isFuzzy()) + { + EditCommand *cmd; + QRegExp fuzzyStr(",\\s*fuzzy"); + + int offset; + offset=comment.find(fuzzyStr); + while(offset>=0) + { + cmd = new DelTextCmd(offset,fuzzyStr.cap(),0); + cmd->setPart(Comment); + editList.append(cmd); + + comment.remove(offset,fuzzyStr.cap().length()); + + offset=comment.find(fuzzyStr,offset+1); + } + + // remove empty comment lines + if( comment.contains( QRegExp("^#\\s*$") )) + { + cmd = new DelTextCmd(0,comment,0); + cmd->setPart(Comment); + editList.append(cmd); + + comment=""; + } + if( comment.contains( QRegExp("\n#\\s*$") )) + { + offset=comment.find( QRegExp("\n#\\s*$") ); + while(offset>=0) + { + cmd = new DelTextCmd(offset,comment.mid(offset),0); + cmd->setPart(Comment); + editList.append(cmd); + + comment.remove(offset,comment.length()-offset); + + offset=comment.find( QRegExp("\n#\\s*$"), offset+1 ); + } + } + if( comment.contains( QRegExp("\n#\\s*\n") )) + { + offset=comment.find( QRegExp("\n#\\s*\n") )+1; + while(offset>=0) + { + int endIndex=comment.find("\n",offset)+1; + + cmd = new DelTextCmd(offset,comment.mid(offset,endIndex-offset),0); + cmd->setPart(Comment); + editList.append(cmd); + + comment.remove(offset,endIndex-offset); + + offset=comment.find( QRegExp("\n#\\s*\n"), offset+1 ); + } + } + + if(doIt) + d->_comment=comment; + + } + + return editList; +} + + + +QPtrList<EditCommand> CatalogItem::addFuzzy(bool doIt) +{ + QPtrList<EditCommand> editList; + editList.setAutoDelete(false); + + + if(!isFuzzy()) + { + EditCommand *cmd; + int offset=d->_comment.length(); + + QString addStr; + if(offset > 0 && d->_comment[offset-1] != '\n') + { + addStr='\n'; + } + addStr+="#, fuzzy"; + + cmd = new InsTextCmd(offset,addStr,0); + cmd->setPart(Comment); + editList.append(cmd); + + + if(doIt) + d->_comment+=addStr; + } + + return editList; +} + + +void CatalogItem::processCommand(EditCommand* cmd, bool undo) +{ + kdDebug(KBABEL) << "CatalogItem::processCommand" << endl; + if(cmd->terminator()!=0) + return; + + DelTextCmd* delcmd = (DelTextCmd*) cmd; + + bool ins = true; + if (delcmd->type() == EditCommand::Delete ) + ins = undo; + else if (delcmd->type() == EditCommand::Insert ) + ins = !undo; + else + { + kdDebug(KBABEL) << "what kind of edit command is this?" << endl; + return; + } + + if ( ins ) + { + if(delcmd->part()==Msgstr) + { + if(delcmd->pluralNumber >= (int)d->_msgstr.count() ) + { + for( uint i = d->_msgstr.count() ; i < (uint)(delcmd->pluralNumber+1) ; i++ ) + d->_msgstr.append( "" ); + } + + (*(d->_msgstr).at(delcmd->pluralNumber)).insert(delcmd->offset,delcmd->str); + } + else if(delcmd->part()==Comment) + { + d->_comment.insert(delcmd->offset,delcmd->str); + } + } + else + { // del + if(delcmd->part()==Msgstr) + { + if(delcmd->pluralNumber >= (int)d->_msgstr.count() ) + { + for( uint i = d->_msgstr.count() ; i < (uint)(delcmd->pluralNumber+1) ; i++ ) + d->_msgstr.append( "" ); + } + + (*(d->_msgstr).at(delcmd->pluralNumber)).remove(delcmd->offset,delcmd->str.length()); + } + else if(delcmd->part()==Comment) + { + d->_comment.remove(delcmd->offset,delcmd->str.length()); + } + } +} + + +void CatalogItem::clear() +{ + if( !d ) d = new CatalogItemPrivate(); + else { + d->_msgid.clear(); + d->_msgstr.clear(); + d->_errors.clear(); + + d->_tagList.clear(); + d->_argList.clear(); + } + d->_comment=""; + d->_msgctxt=""; + d->_valid=true; + d->_gettextPluralForm=false; + d->_haveTagList=false; + d->_haveArgList=false; + + d->_msgid.append(""); + d->_msgstr.append(""); +} + +void CatalogItem::operator=(const CatalogItem& rhs) +{ + d->_comment = rhs.d->_comment; + d->_msgctxt = rhs.d->_msgctxt; + d->_msgid = rhs.d->_msgid; + d->_msgstr = rhs.d->_msgstr; + d->_valid = rhs.d->_valid; + d->_errors = rhs.d->_errors; + d->_gettextPluralForm = rhs.d->_gettextPluralForm; +} + +QString CatalogItem::nextError() const +{ + return d->_errors.first(); +} + +void CatalogItem::clearErrors() +{ + d->_errors.clear(); +} + +void CatalogItem::appendError(const QString& error ) +{ + if( !d->_errors.contains( error ) ) + d->_errors.append(error); +} + +void CatalogItem::removeError(const QString& error ) +{ + d->_errors.remove(error); +} + +Project::Ptr CatalogItem::project() const +{ + return d->_project; +} + +void CatalogItem::setProject( Project::Ptr project ) +{ + d->_project = project; +} + +// kate: space-indent on; indent-width 4; replace-tabs on; diff --git a/kbabel/common/catalogitem.h b/kbabel/common/catalogitem.h new file mode 100644 index 00000000..91e6cc25 --- /dev/null +++ b/kbabel/common/catalogitem.h @@ -0,0 +1,220 @@ +/* **************************************************************************** + This file is part of KBabel + + Copyright (C) 1999-2000 by Matthias Kiefer <matthias.kiefer@gmx.de> + 2002-2003 by Stanislav Visnovsky <visnovsky@kde.org> + Copyright (C) 2006 by Nicolas GOUTTE <goutte@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the 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. + +**************************************************************************** */ +#ifndef CATALOGITEM_H +#define CATALOGITEM_H + +#include <qstringlist.h> +#include <qptrlist.h> + +#include "pluralforms.h" +#include "editcmd.h" +#include "catalogfileplugin.h" +#include "kbproject.h" + +class QTextStream; +class GettextFlexLexer; + +namespace KBabel +{ + +class RegExpExtractor; +class CatalogItemPrivate; + +/** +* This class represents an entry in a catalog. +* It contains the comment, the Msgid and the Msgstr. +* It defines some functions to query the state of the entry +* (fuzzy, untranslated, cformat). +* +* @short Class, representing an entry in a catalog +* @author Matthias Kiefer <matthias.kiefer@gmx.de> +*/ +class KDE_EXPORT CatalogItem +{ + +public: + /** Constructor for empty item*/ + CatalogItem(Project::Ptr project = 0); + /** Copy constructor */ + CatalogItem(const CatalogItem&); + /** Destructor */ + ~CatalogItem(); + + /** + * returns true, if the translation is fuzzy, this means + * if the string ", fuzzy" is contained in the comment + */ + bool isFuzzy() const; + + /** + * returns true, if the translation in c-format, this means + * if the string ", c-format" is contained in the comment + * @since 1.11.2 (KDE 3.5.2) not only "c-format" is supported but also + * "possible-c-format" (from the debug parameter of xgettext) + */ + bool isCformat() const; + + /** + * returns true, if the string ", no-c-format" is contained in the comment + */ + bool isNoCformat() const; + + /** + * returns true, if the translation in qt-format, this means + * if the string ", qt-format" is contained in the comment + * @since 1.11.2 (KDE 3.5.2) + */ + bool isQtformat() const; + + /** + * returns true, + * if the string ", no-qt-format" is contained in the comment + * @since 1.11.2 (KDE 3.5.2) + */ + bool isNoQtformat() const; + + /** + * returns true, if the Msgid is untranslated, this means the + * Msgstr is empty + */ + bool isUntranslated() const; + + /** + * @return type of the plural form, is any + */ + PluralFormType pluralForm() const; + + bool isValid() const; + + void setSyntaxError(bool); + + /** returns the number of lines, the entry will need in a file */ + int totalLines() const; + + /** + * removes the string ", fuzzy" from the comment + * @param doIt if false, the comment is not changed, just the + * commands for doing it are calculated + */ + QPtrList<EditCommand> removeFuzzy(bool doIt=true); + + /** + * adds the string ", fuzzy" to the comment + * @param doIt if false, the comment is not changed, just the + * commands for doing it are calculated + */ + QPtrList<EditCommand> addFuzzy(bool doIt=true); + + + /** cleares the item */ + void clear(); + + + /** returns the comment of this entry */ + QString comment() const; + /** returns the msgctxt of this entry */ + QString msgctxt(const bool noNewlines = false) const; + /** returns the msgid of the entry */ + QStringList msgid(const bool noNewlines = false) const; + /** returns the msgstr of the entry */ + QStringList msgstr(const bool noNewlines = false) const; + + /** + * @param pluralNr If this item is a gettext plural form item, + * it returns the plural form number pluralNr, else always the msgstr + * is returned + * @return the msgstr as list + */ + QStringList msgstrAsList(int pluralNr=0) const; + + void setComment(QString com); + void setMsgctxt(QString msg); + void setMsgid(QString msg); + void setMsgid(QStringList msg); + void setMsgstr(QString msg); + void setMsgstr(QStringList msg); + + void setGettextPluralForm( bool _gettextPlurals ); + + void processCommand(EditCommand* cmd, bool undo=false); + + Project::Ptr project() const; + + + /** @return a list of tags in the msgid */ + QStringList tagList( RegExpExtractor& tagExtractor ); + + /** @return a list of args in the msgid */ + QStringList argList( RegExpExtractor& argExtractor); + + /** + * @return the list of all errors of this item + */ + QStringList errors() const; + + QString nextError() const; + void clearErrors(); + void removeError(const QString& error); + void appendError(const QString& error); + + /** + * makes some sanity checks and set status accordingly + * @return the new status of this item + * @see CatalogItem::Error + * @param accelMarker a char, that marks the keyboard accelerators + * @param contextInfo a regular expression, that determines what is + * the context information + * @param singularPlural a regular expression, that determines what is + * string with singular and plural form + * @param neededLines how many lines a string with singular-plural form + * must have + */ + int checkErrors(QChar accelMarker, const QRegExp& contextInfo + , const QRegExp& singularPlural, const int neededLines); + + + void operator=(const CatalogItem& rhs); + +private: + void setProject( Project::Ptr project ); + + friend class Catalog; + +private: + CatalogItemPrivate* d; +}; + +} + +#endif // CATALOGITEM_H diff --git a/kbabel/common/catalogitem_private.h b/kbabel/common/catalogitem_private.h new file mode 100644 index 00000000..15c81fc7 --- /dev/null +++ b/kbabel/common/catalogitem_private.h @@ -0,0 +1,90 @@ +/* **************************************************************************** + This file is part of KBabel + + Copyright (C) 1999-2000 by Matthias Kiefer + <matthias.kiefer@gmx.de> + 2002 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., 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. + +**************************************************************************** */ +#ifndef CATALOGITEMPRIVATE_H +#define CATALOGITEMPRIVATE_H + +#include <qstringlist.h> + +namespace KBabel +{ + +/** +* This class represents data for an entry in a catalog. +* It contains the comment, the Msgid and the Msgstr. +* It defines some functions to query the state of the entry +* (fuzzy, untranslated, cformat). +* +* @short Class, representing an entry in a catalog +* @author Matthias Kiefer <matthias.kiefer@gmx.de> +* @author Stanislav Visnovsky <visnovsky@kde.org> +*/ + +class CatalogItemPrivate +{ + +public: + + QString _comment; + QString _msgctxt; + QStringList _msgid; + QStringList _msgstr; + + QStringList _tagList; + QStringList _argList; + + bool _valid; + bool _haveTagList; + bool _haveArgList; + QStringList _errors; + + /** flag, if this entry is a plural forms entry */ + bool _gettextPluralForm; + + friend class CatalogItem; + + KBabel::Project::Ptr _project; + + CatalogItemPrivate() + { + _comment = ""; + _valid=false; + _haveTagList=false; + _haveArgList=false; + _gettextPluralForm=false; + } +}; + +} + +#endif // CATALOGITEMPRIVATE_H diff --git a/kbabel/common/catalogsettings.cpp b/kbabel/common/catalogsettings.cpp new file mode 100644 index 00000000..480f4cb9 --- /dev/null +++ b/kbabel/common/catalogsettings.cpp @@ -0,0 +1,258 @@ +/* **************************************************************************** + This file is part of KBabel + + Copyright (C) 1999-2000 by Matthias Kiefer + <matthias.kiefer@gmx.de> + 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., 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 "catalogsettings.h" +#include "kbprojectsettings.h" +#include <kconfig.h> +#include <kdebug.h> +#include <kemailsettings.h> +#include <kglobal.h> +#include <klocale.h> +#include <kprocess.h> +#include <ktempfile.h> + +#include <stdlib.h> + +#include <qfile.h> +#include <qtextcodec.h> +#include <qregexp.h> + +#include <stdlib.h> + +using namespace KBabel; + +QString KBabel::charsetString(const QTextCodec *codec) +{ + if(codec) + { + QString encodingStr = codec->mimeName(); + if ( encodingStr.startsWith("CP " ) ) + encodingStr.remove( 2, 1 ); + else if ( encodingStr.startsWith("IBM " ) ) + encodingStr.replace( "IBM ", "CP" ); + return encodingStr; + } + else + return QString::null; +} + +QString KBabel::GNUPluralForms(const QString& lang) +{ + KTempFile infile, outfile; + + QTextStream* str = infile.textStream(); + + *str << "# SOME DESCRIPTIVE TITLE." << endl; + *str << "# Copyright (C) YEAR Free Software Foundation, Inc." << endl; + *str << "# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR." << endl; + *str << "#" << endl; + *str << "#, fuzzy" << endl; + *str << "msgid \"\"" << endl; + *str << "msgstr \"\"" << endl; + *str << "\"Project-Id-Version: PACKAGE VERSION\\n\"" << endl; + *str << "\"POT-Creation-Date: 2002-06-25 03:23+0200\\n\"" << endl; + *str << "\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"" << endl; + *str << "\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"" << endl; + *str << "\"Language-Team: LANGUAGE <LL@li.org>\\n\"" << endl; + *str << "\"MIME-Version: 1.0\\n\"" << endl; + *str << "\"Content-Type: text/plain; charset=CHARSET\\n\"" << endl; + *str << "\"Content-Transfer-Encoding: ENCODING\\n\"" << endl; + + infile.close(); + + KProcess msginit; + + msginit << "msginit"; + msginit + << "-l" + << lang + << "-i" + << infile.name() + << "-o" + << outfile.name() + << "--no-translator" + << "--no-wrap" ; + + msginit.start( KProcess::Block ); + + QString res(""); + + if( msginit.normalExit() ) + { + // parse out the plural form string + QFile f(outfile.name()); + if( f.open (IO_ReadOnly) ) + { + QTextStream str(&f); + + QString line; + do { + line = str.readLine(); + + if( line.startsWith( "\"Plural-Forms:" ) ) + { + kdDebug() << "Plural form line: " << line << endl; + QRegExp re( "^\"Plural-Forms: *(.*)\\\\n\"" ); + re.search( line ); + res = re.cap(1); + break; + } + } while (!str.atEnd() ); + } + else + { + kdWarning() << "Cannot open the file with plural form definition" << endl; + } + } + + infile.unlink(); + outfile.unlink(); + + return res; +} + +QString KBabel::charsetString(const int e) +{ + QString encodingStr; + + switch(e) + { + case ProjectSettingsBase::Locale: + { + QTextCodec *codec=QTextCodec::codecForLocale(); + if(codec) + encodingStr=charsetString(codec); + else + encodingStr="unknown"; + + break; + } + case ProjectSettingsBase::UTF8: + { + encodingStr="UTF-8"; + break; + } + case ProjectSettingsBase::UTF16: + { + encodingStr="UTF-16"; + break; + } + } + + return encodingStr; +} + +QString Defaults::Identity::authorName() +{ + KEMailSettings emSet; + return emSet.getSetting(KEMailSettings::RealName); +} + +QString Defaults::Identity::authorLocalizedName() +{ + return authorName(); +} + +QString Defaults::Identity::authorEmail() +{ + KEMailSettings emSet; + return emSet.getSetting(KEMailSettings::EmailAddress); +} + +QString Defaults::Identity::mailingList() +{ + QString lang=Defaults::Identity::languageCode(); + int temp=lang.find("_"); + lang=lang.left(temp); + return lang+"@li.org"; +} + +QString Defaults::Identity::languageCode() +{ + // first try to get the language from KDE settings + KLocale *locale = KGlobal::locale(); + QString lang; + if(locale) + { + lang=locale->languageList().first(); + } + + if(lang.isEmpty()) + { + lang=getenv("LC_ALL"); + if(lang.isEmpty()) + { + lang=getenv("LC_MESSAGES"); + if(lang.isEmpty()) + { + lang=getenv("LANG"); + } + } + } + + return lang; +} + +QString Defaults::Identity::timezone() +{ + QString timezone=getenv("TIMEZONE"); + if(timezone.isEmpty()) + timezone="GMT"; + + return timezone; +} + +QStringList Defaults::Tag::tagExpressions() +{ + QStringList list; + + list.append("</[A-Za-z0-9\\n]+>"); + list.append("<[A-Za-z0-9\\n]+[^>]*/?>"); + list.append("http:\\/\\/[a-zA-Z0-9\\.\\-_/~]+"); + list.append("mailto:[a-z0-9\\.\\-_]+@[a-z0-9\\.\\-_]+"); + list.append("<?[a-z0-9\\.\\-_]+@[a-z0-9\\.\\-_]+>?"); + list.append("&[a-z,A-Z,\\-,0-9,#\\.]*;"); + + return list; +} + +QStringList Defaults::Tag::argExpressions() +{ + QStringList list; + + list.append("%[ndioxXucsfeEgGp]"); + list.append("%([0-9]+(\\$))?[-+'#0]?[0-9]*(.[0-9]+)?[hlL]?[dioxXucsfeEgGp]"); + list.append("%[0-9]+"); + + return list; +} diff --git a/kbabel/common/catalogsettings.h b/kbabel/common/catalogsettings.h new file mode 100644 index 00000000..f87ec673 --- /dev/null +++ b/kbabel/common/catalogsettings.h @@ -0,0 +1,176 @@ +/* **************************************************************************** + 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., 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. +**************************************************************************** */ +#ifndef CATALOGSETTINGS_H +#define CATALOGSETTINGS_H + +#include <qstring.h> +#include <qregexp.h> +#include <qdatetime.h> +#include <kdemacros.h> + +class QTextCodec; +class QStringList; + +namespace KBabel +{ + +struct KDE_EXPORT SaveSettings +{ + bool autoUpdate; + bool updateLastTranslator; + bool updateRevisionDate; + bool updateLanguageTeam; + bool updateCharset; + bool updateEncoding; + + bool updateProject; + bool updateDescription; + QString descriptionString; + bool updateTranslatorCopyright; + int FSFCopyright; + + int encoding; + bool useOldEncoding; + + Qt::DateFormat dateFormat; + QString customDateFormat; + + QString projectString; + + bool autoSyntaxCheck; + bool saveObsolete; + + int autoSaveDelay; +}; + +struct KDE_EXPORT IdentitySettings +{ + QString authorName; + QString authorLocalizedName; + QString authorEmail; + QString languageName; + QString languageCode; + QString mailingList; + QString timeZone; + + /** + * The number of plural forms. If <= 0 the number is determined + * automatically. + */ + int numberOfPluralForms; + /** + * Whether the %n argument should be always present in translation + */ + bool checkPluralArgument; + + QString gnuPluralFormHeader; +}; + + +struct KDE_EXPORT MiscSettings +{ + /** + * The char, that marks keyboard accelerators. + * Default is '&' as used by Qt + */ + QChar accelMarker; + + /** + * The regular expression for what is context information. + * Default is "^_:.+" as used in KDE + */ + QRegExp contextInfo; + + /** + * The regular expression for strings that contain a message for + * singular and one for plural + */ + QRegExp singularPlural; + + /** + * The method used for compresion of email attachments. Use + * tar/bzip2 if true, tar/gzip if false. + * Default is true. + */ + bool useBzip; + + /** + * Use compression for single file attachments. + * Default is true. + */ + bool compressSingleFile; +}; + +struct TagSettings +{ + /** + * A list of regular expressions defining tags + */ + QStringList tagExpressions; + /** + * A list of regular expressions defining arguments + */ + QStringList argExpressions; +}; + +KDE_EXPORT QString charsetString(const int encoding); +KDE_EXPORT QString charsetString(const QTextCodec *codec); +KDE_EXPORT QString GNUPluralForms(const QString& lang); + +namespace Defaults +{ + class KDE_EXPORT Identity + { + public: + static QString authorName(); + static QString authorLocalizedName(); + static QString authorEmail(); + static QString languageCode(); + static QString mailingList(); + static QString timezone(); + }; + + class KDE_EXPORT Tag + { + public: + static QStringList tagExpressions(); + static QStringList argExpressions(); + }; + +} + +} + + +#endif diff --git a/kbabel/common/catalogview.h b/kbabel/common/catalogview.h new file mode 100644 index 00000000..03056c43 --- /dev/null +++ b/kbabel/common/catalogview.h @@ -0,0 +1,64 @@ +/* **************************************************************************** + This file is part of KBabel + + Copyright (C) 1999-2000 by Matthias Kiefer + <matthias.kiefer@gmx.de> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 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. + +**************************************************************************** */ +#ifndef CATALOGVIEW_H +#define CATALOGVIEW_H + +namespace KBabel +{ + +class EditCommand; + +/** + * This class represents a base class for a @ref Catalog view. It declares + * an abstract method @ref update to be redefined by the actual + * view. Using this method, the catalog the view is registered for + * will inform the views about changes in the catalog. + * + * @short This class represents a base class for a catalog view. + * @author Matthias Kiefer <matthias.kiefer@gmx.de> + */ +class CatalogView +{ +public: + /** The only method to be redefined in inherited classes. + * This method is called by @ref Catalog to update the view + * in case there are changes. + * @param cmd the command line performed on the catalog + * @param undo this is used for undo, reverse the operation + */ + virtual void update(EditCommand* cmd, bool undo=false)=0; +}; + +} + +#endif // CATALOGVIEW_H diff --git a/kbabel/common/diff.cpp b/kbabel/common/diff.cpp new file mode 100644 index 00000000..7a54e4d3 --- /dev/null +++ b/kbabel/common/diff.cpp @@ -0,0 +1,465 @@ +/* ************************************************************************** + This file is part of KBabel + + Copyright (C) 2001 by Matthias Kiefer <kiefer@kde.org> + charDiff algorithm by Wolfram Diestel <wolfram@steloj.de> + wordDiff algorithm by Nick Shaforostoff <shafff@ukr.net> + + 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 "diff.h" + +#include <kdebug.h> + + +LCSprinter::LCSprinter(const QStringList &s_1, const QStringList &s_2, QValueVector<LCSMarker> *b_, const uint nT_, uint index):s1(s_1),s2(s_2),b(b_),nT(nT_) +{ + it1=s1.begin(); + it2=s2.begin(); + printLCS(index); +} + +void LCSprinter::printLCS(uint index) +{ + //fprintf(stderr,"%2d. %2d. %2d. %2d\n",(uint)(*b)[index],nT,index%nT, index); + if (index % nT == 0 || index < nT) + { + //original LCS algo doesnt have to deal with ins before first common + uint bound = index%nT; + for (index=0; index<bound; ++index) + { + resultString.append("<KBABELADD>"); + resultString.append(*it2); + ++it2; + resultString.append("</KBABELADD>"); + } + + return; + } + + if (ARROW_UP_LEFT == (*b)[index]) + { + printLCS(index-nT-1); + resultString.append(*it1); + ++it1; + ++it2; + return; + } + else if (ARROW_UP == (*b)[index]) + { + printLCS(index-nT); + resultString.append("<KBABELDEL>"); + resultString.append(*it1); + ++it1; + resultString.append("</KBABELDEL>"); + return; + } + else + { + printLCS(index-1); + resultString.append("<KBABELADD>"); + resultString.append(*it2); + ++it2; + resultString.append("</KBABELADD>"); + return; + } + +} + + + +QString wordDiff(const QString& str1, const QString& str2) +{ + //separate punctuation marks etc from words as _only_ they may have changed + QStringList s1, s2; + + uint i=0; + uint j=0; + uint l1=str1.length(); + uint l2=str2.length(); + QString temp; + temp.reserve(16); + /* + while ( i<l1 ) + { + if (str1[i]==QChar(0x003C) && (str1[i+1]==QChar(0x002F) || str1[i+1].isLetter()) ) // 0x003C="<", 0x002F="/" + {// skip tag + while(i<l1 && str1[i]!=QChar(0x003E)) temp += str1[i++]; + if (i++ <l1) temp += QChar(0x003E); + s1.append(temp); + temp.truncate(0); + } + + while ( i<l1 && str1[i].isLetter() ) + { + temp += str1[i++]; + } + if (!temp.isEmpty()) + { + s1.append(temp); + temp.truncate(0); + }; + + if (str1[i]==QChar(0x003C) && (str1[i+1]==QChar(0x002F) || str1[i+1].isLetter()) ) // 0x003C="<", 0x002F="/" + {// skip tag + while(i<l1 && str1[i]!=QChar(0x003E)) temp += str1[i++]; + if (i++ <l1) temp += QChar(0x003E); + s1.append(temp); + temp.truncate(0); + } + + while ( i<l1 && !(str1[i].isLetter() || str1[i]==QChar(0x003C)) ) + { + temp += str1[i++]; + } + if (!temp.isEmpty()) + { + s1.append(temp); + temp.truncate(0); + }; + } + + i=0; + while ( i<l2 ) + { + if (str2[i]==QChar(0x003C) && (str2[i+1]==QChar(0x002F) || str2[i+1].isLetter()) ) // 0x003C="<", 0x002F="/" + {// skip tag + while(i<l2 && str2[i]!=QChar(0x003E)) temp += str2[i++]; + if (i++ <l2) temp += QChar(0x003E); + s2.append(temp); + temp.truncate(0); + } + + while ( i<l2 && str2[i].isLetter() ) + { + temp += str2[i++]; + } + if (!temp.isEmpty()) + { + s2.append(temp); + temp.truncate(0); + }; + //FIXME bounds + if (str2[i]==QChar(0x003C) && (str2[i+1]==QChar(0x002F) || str2[i+1].isLetter()) ) // 0x003C="<", 0x002F="/" + {// skip tag + while(i<l2 && str2[i]!=QChar(0x003E)) temp += str2[i++]; + if (i++ <l2) temp += QChar(0x003E); + s2.append(temp); + temp.truncate(0); + } + + while ( i<l2 && !(str2[i].isLetter() || str2[i]==QChar(0x003C)) ) + { + temp += str2[i++]; + } + if (!temp.isEmpty()) + { + s2.append(temp); + temp.truncate(0); + }; + } +*/ + + while ( i<l1 ) + { + while ( i<l1 && str1[i].isLetter() ) + { + temp += str1[i++]; + } + if (!temp.isEmpty()) + { + s1.append(temp); + temp.truncate(0); + }; + + while ( i<l1 && !str1[i].isLetter() ) + { + s1.append(QString(str1[i++])); + } + } + + i=0; + while ( i<l2 ) + { + while ( i<l2 && str2[i].isLetter() ) + { + temp += str2[i++]; + } + if (!temp.isEmpty()) + { + s2.append(temp); + temp.truncate(0); + }; + + while ( i<l2 && !str2[i].isLetter() ) + { + s2.append(QString(str2[i++])); + } + } + + uint mX = s1.count(); + uint nY = s2.count(); + + uint mT = mX+1; + uint nT = nY+1; + + QValueVector<LCSMarker> b(mT*nT, NOTHING); + QValueVector<uint> c(mT*nT, 0); + + +// calculate the LCS + b[0] = FINAL; + uint index_cache; + QStringList::iterator it1, it2; + + for (i=1, it1 = s1.begin(); i<mT; ++i, ++it1) + { + for (j=1, it2 = s2.begin(); j<nT; ++j, ++it2) + { + index_cache = i*nT+j; + if ((*it1)==(*it2)) + { + c[index_cache] = c[index_cache-nT-1] + 1; + b[index_cache] = ARROW_UP_LEFT; + } + else if (c[index_cache-nT] >= c[index_cache-1]) + { + c[index_cache] = c[index_cache-nT]; + b[index_cache] = ARROW_UP; + } + else + { + c[index_cache] = c[index_cache-1]; + b[index_cache] = ARROW_LEFT; + } + } + } + + c.clear(); + + + LCSprinter printer(s1, s2, &b, nT, index_cache); + + /*//debug + fprintf(stderr,"b:\n"); + for (i=0; i<=mT; i++) { + for (j=0; j<nY; j++) { +// fprintf(stderr,"[%3d]%1d.",i*nT+j,(uint) b[i*nT+j]); + fprintf(stderr,"%1d.",(uint) b[i*nT+j]); +} + fprintf(stderr,"%1d\n",(uint) b[i*nT+j]); +} + fprintf(stderr,"\n"); + + // print the c table + fprintf(stderr,"c:\n"); + for (i=0; i<=mX; i++) { + for (j=0; j<nY; j++) { + fprintf(stderr,"%2d.",(uint) c[i*nY+j]); +} + fprintf(stderr,"%2d\n",(uint) c[i*nY+j]); +} + fprintf(stderr,"\n"); + */ + + return printer.getString(); +} + + + + +/* +// old algorithm by Wolfram Diestel <wolfram@steloj.de> +QString Diff::charDiff(QString s1, QString s2) +{ + int n = 3; + s1+="xxx"; + s2+="xxx"; + + int pos1=0; + int pos2=0; + int len1 = s1.length(); + int len2 = s2.length(); + QString resultstr; + bool found = true; + bool swap = false; + while (found && pos1+n < len1 && pos2+n < len2) + { + // while strings are equal, increase positions + int len = 0; + while (pos1+len < len1 && pos2+len < len2 && + (s1[pos1+len] == s2[pos2+len])) + { + len++; + } + + // write first result + resultstr += s1.mid(pos1,len); + pos1 += len; + pos2 += len; + + // If strings are not equal try to find equal substrings of + // length n. For this increase positions alternating like + // 0 1, 1 0, 0 2, 1 1, 2 0, 0 3, 1 2, 2 1, 3 0 etc. + + int x = 1; + found = false; + + // If one position is already at the end stop processing + if (pos1+n >= len1 || pos2+n >= len2) + { + break; + } + + + // evtl. exchange + if (len2 - pos2 < len1 - pos1) + { + swap=true; + + int pos = pos1; + pos1 = pos2; + pos2 = pos; + + QString s = s1; + s1 = s2; + s2 = s; + + int len = len1; + len1 = len2; + len2 = len; + } + + while( !found && (pos1+x < len1 || pos2+x <len2)) + { + int p1; + int p2; + for(p1 = 0, p2 = x; p1 <= x; p1++, p2--) + { + if( pos1+p1+n >= len1 || pos2+p2+n >= len2) + break; + + if(s1.mid(pos1+p1,n) == s2.mid(pos2+p2,n)) + { + found = true; + + // evtl. exchange back + if(swap) + { + int pos = pos1; + pos1 = pos2; + pos2 = pos; + + QString s = s1; + s1 = s2; + s2 = s; + + int len = len1; + len1 = len2; + len2 = len; + + int p = p1; + p1 = p2; + p2 = p; + + swap=false; + } + + + // write current result + if (p1 > 0) + { + resultstr += "<KBABELDEL>"; + resultstr += s1.mid(pos1,p1); + resultstr += "</KBABELDEL>"; + pos1 += p1; + } + + if (p2 > 0 ) + { + resultstr += "<KBABELADD>"; + resultstr += s2.mid(pos2,p2); + resultstr += "</KBABELADD>"; + pos2 += p2; + } + + break; + } + } + + x++; + + } + + } + + bool removed=false; + + // evtl. exchange back + if(swap) + { + int pos = pos1; + pos1 = pos2; + pos2 = pos; + + QString s = s1; + s1 = s2; + s2 = s; + + int len = len1; + len1 = len2; + len2 = len; + + swap=false; + } + + // if there is a rest, add it + if(pos1+n < len1) + { + resultstr += "<KBABELDEL>"; + resultstr += s1.mid(pos1,len1-pos1-n); + resultstr += "</KBABELDEL>"; + removed=true; + } + + if(pos2+n < len2) + { + resultstr += "<KBABELADD>"; + resultstr += s2.mid(pos2,len2-pos2-n); + resultstr += "</KBABELADD>"; + removed=true; + } + + if( !removed ) + { + resultstr.truncate( resultstr.length()-3 ); + } + + return resultstr; +} +*/ diff --git a/kbabel/common/diff.h b/kbabel/common/diff.h new file mode 100644 index 00000000..4e7dfb72 --- /dev/null +++ b/kbabel/common/diff.h @@ -0,0 +1,88 @@ +/* ************************************************************************** + This file is part of KBabel + + Copyright (C) 2001 by Matthias Kiefer <kiefer@kde.org> + charDiff algorithm by Wolfram Diestel <wolfram@steloj.de> + wordDiff algorithm by Nick Shaforostoff <shafff@ukr.net> + (based on Markus Stengel's GPL implementation of LCS-Delta algorithm as it is described in "Introduction to Algorithms", MIT Press, 2001, Second Edition, written by Thomas H. Cormen et. al. It uses dynamic programming to solve the Longest Common Subsequence (LCS) problem. - http://www.markusstengel.de/text/en/i_4_1_5_3.html) + + 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. + +************************************************************************** */ +#ifndef DIFF_H +#define DIFF_H + +#include <qvaluevector.h> +#include <qstringlist.h> + +typedef enum +{ + NOTHING = 0, + ARROW_UP = 1, + ARROW_LEFT = 2, + ARROW_UP_LEFT = 3, + FINAL = 4 +} LCSMarker; + + +/** + * Word-by-word diff algorithm + * + * @short Word-by-word diff algorithm + * @author Nick Shaforostoff <shafff@ukr.net> + */ + QString wordDiff(const QString& oldString, const QString& newString); + + +/** + * This class is for keeping "global" params of recursive function + * + * @short Class for keeping "global" params of recursive function + * @author Nick Shaforostoff <shafff@ukr.net> + */ + class LCSprinter +{ + public: + LCSprinter(const QStringList &s_1, const QStringList &s_2, QValueVector<LCSMarker>* b_, const uint nT_, uint index); + void printLCS(uint index); + inline QString getString(); + + ~LCSprinter() {}; + private: + QStringList s1, s2, resultString; + uint nT;//for use 1d vector as 2d + QValueVector<LCSMarker> *b; + QStringList::iterator it1, it2; +}; + + +inline QString LCSprinter::getString() +{ + return resultString.join("").replace(QChar('\n'), ""); //w/o replace we'd get whole line colored +} + +#endif // DIFF_H + diff --git a/kbabel/common/editcmd.cpp b/kbabel/common/editcmd.cpp new file mode 100644 index 00000000..1643ed0b --- /dev/null +++ b/kbabel/common/editcmd.cpp @@ -0,0 +1,106 @@ +/* **************************************************************************** + This file is part of KBabel + + Copyright (C) 1999-2000 by Matthias Kiefer + <matthias.kiefer@gmx.de> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 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 "editcmd.h" +#include <qregexp.h> + +using namespace KBabel; + +EditCommand::EditCommand() + :_part(UndefPart), + _index(-1) +{ +} + +EditCommand::EditCommand(const int index, const Part part) + :_part(part), + _index(index) +{ +} + + + // have to handle deletion of current selection +DelTextCmd::DelTextCmd(int _offset, const QString &_str, int _pluralNumber ) + : EditCommand(), + offset( _offset ), + str ( _str ), + pluralNumber( _pluralNumber ) +{ +} + +bool DelTextCmd::merge( EditCommand* other) +{ + if(other->terminator()!=0) + return false; + + DelTextCmd* o = (DelTextCmd*)other; + + if ( o->index() == index() && o->part() == part() + && o->type() == type() && o->pluralNumber==pluralNumber ) + { + DelTextCmd* o = (DelTextCmd*) other; + if ( offset + int(str.length()) == o->offset && !str.contains(QRegExp("^\\s$")) ) + { + o->str.prepend( str ); + o->offset = offset; + return true; + } + } + return false; +} + + + +InsTextCmd::InsTextCmd(int offset,const QString &str, int pluralNumber ) + : DelTextCmd( offset, str, pluralNumber ) +{ +} + +bool InsTextCmd::merge( EditCommand* other) +{ + if(other->terminator()!=0) + return false; + + DelTextCmd* o = (DelTextCmd*)other; + + if ( o->index()==index() && o->part() == part() + && o->type() == type() && o->pluralNumber==pluralNumber ) + { + if ( offset == o->offset + int(o->str.length()) && !str.contains(QRegExp("^\\s$")) ) + { + o->str += str; + return true; + } + } + return false; +} + diff --git a/kbabel/common/editcmd.h b/kbabel/common/editcmd.h new file mode 100644 index 00000000..458d2c57 --- /dev/null +++ b/kbabel/common/editcmd.h @@ -0,0 +1,112 @@ +/* **************************************************************************** + This file is part of KBabel + + Copyright (C) 1999-2000 by Matthias Kiefer + <matthias.kiefer@gmx.de> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 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. +**************************************************************************** */ +#ifndef EDITCMD_H +#define EDITCMD_H + +#include "itempart.h" + +#include <qstring.h> +#include <kdemacros.h> + +namespace KBabel +{ + +class KDE_EXPORT EditCommand +{ +public: + enum Commands { Invalid, Begin, End, Insert, Delete }; + + EditCommand(); + EditCommand( const int index, const Part part ); + virtual ~EditCommand() {}; + virtual Commands type() const { return Invalid; } + virtual int terminator() const { return 0; } + + int index() const { return _index; } + void setIndex( int index ) { _index=index; } + Part part() const { return _part; } + void setPart(Part part) {_part=part;} + + virtual bool merge( EditCommand* ) { return false;} + +private: + Part _part; + int _index; +}; + + +class KDE_EXPORT BeginCommand : public EditCommand +{ +public: + BeginCommand( const int index, const Part part ) : EditCommand(index,part) {} + virtual Commands type() const { return Begin; } + virtual int terminator() const { return 1; } +}; + + +class KDE_EXPORT EndCommand : public EditCommand +{ +public: + EndCommand( const int index, const Part part ) : EditCommand(index,part) {} + virtual Commands type() const { return End; } + virtual int terminator() const { return -1; } +}; + + + +class KDE_EXPORT DelTextCmd : public EditCommand +{ +public: + int offset; + QString str; + int pluralNumber; + + // have to handle deletion of current selection + DelTextCmd(int offset, const QString &str, int pluralNumber ); + virtual Commands type() const { return Delete; } + + bool merge( EditCommand* other); +}; + +class KDE_EXPORT InsTextCmd : public DelTextCmd +{ + +public: + InsTextCmd(int offset,const QString &str, int pluralNumber ); + virtual Commands type() const { return Insert; } + + bool merge( EditCommand* other); +}; + +} + +#endif // SETTINGS_H diff --git a/kbabel/common/exportplugin.cpp b/kbabel/common/exportplugin.cpp new file mode 100644 index 00000000..99a727f8 --- /dev/null +++ b/kbabel/common/exportplugin.cpp @@ -0,0 +1,81 @@ +/* **************************************************************************** + This file is part of KBabel + + Copyright (C) 2002-2003 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., 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 "catalogfileplugin.h" + +#include <qstringlist.h> + +#include <kdebug.h> +#include <ktrader.h> + +using namespace KBabel; + +struct KBabel::CatalogExportPluginPrivate +{ + bool _stop; +}; + +CatalogExportPlugin::CatalogExportPlugin(QObject* parent, const char* name) : QObject(parent,name) +{ + d = new CatalogExportPluginPrivate; + d->_stop=false; +} + +CatalogExportPlugin::~CatalogExportPlugin() +{ + delete d; +} + +QStringList CatalogExportPlugin::availableExportMimeTypes() +{ + QStringList result; + + KTrader::OfferList offers = KTrader::self()->query("KBabelFilter", "exist [X-KDE-Export]"); + + for( KTrader::OfferListIterator ptr = offers.begin(); ptr!=offers.end() ; ++ptr ) + { + result += (*ptr)->property("X-KDE-Export").toStringList(); + } + + return result; +} + +void CatalogExportPlugin::stop() +{ + d->_stop = true; +} + +bool CatalogExportPlugin::isStopped() const +{ + return d->_stop; +} diff --git a/kbabel/common/findoptions.h b/kbabel/common/findoptions.h new file mode 100644 index 00000000..45c29e25 --- /dev/null +++ b/kbabel/common/findoptions.h @@ -0,0 +1,86 @@ +/* **************************************************************************** + This file is part of KBabel + + Copyright (C) 1999-2000 by Matthias Kiefer + <matthias.kiefer@gmx.de> + 2001 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., 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. + +**************************************************************************** */ +#ifndef FINDOPTIONS_H +#define FINDOPTIONS_H + +#include <qregexp.h> + +class QString; +class QChar; + +namespace KBabel +{ + +class FindOptions +{ +public: + QString findStr; + + bool caseSensitive; + bool wholeWords; + bool fromCursor; + bool backwards; + bool isRegExp; + + bool inMsgid; + bool inMsgstr; + bool inComment; + + bool ignoreAccelMarker; + bool ignoreContextInfo; + + bool askForNextFile; + bool askForSave; // whether should ask before saving or save automatically when loading next file + bool inAllFiles; // whether search should be done in all files + bool inMarkedFiles; // whether search should be restricted to marked ones + bool inTemplates; + + // these are non-persistent options - not saved into configuration database + bool askFile; // whether findNext should ask catalog manager for next file + QRegExp contextInfo; // actual settings of context info regular expression + QChar accelMarker; // actual settings of accelerator marker + +}; + +class ReplaceOptions : public FindOptions +{ +public: + QString replaceStr; + bool ask; +}; + +} + +#endif diff --git a/kbabel/common/importplugin.cpp b/kbabel/common/importplugin.cpp new file mode 100644 index 00000000..d763e670 --- /dev/null +++ b/kbabel/common/importplugin.cpp @@ -0,0 +1,194 @@ +/* **************************************************************************** + This file is part of KBabel + + Copyright (C) 2002-2003 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., 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 "catalogfileplugin.h" +#include "importplugin_private.h" + +#include "catalog.h" + +#include <qstringlist.h> + +#include <kdebug.h> +#include <ktrader.h> + +using namespace KBabel; + +CatalogImportPlugin::CatalogImportPlugin(QObject* parent, const char* name) : QObject(parent,name) +{ + d = new CatalogImportPluginPrivate; + d->_catalog = 0; + d->_started = false; + d->_stopped = false; +} + +CatalogImportPlugin::~CatalogImportPlugin() +{ + delete d; +} + +void CatalogImportPlugin::appendCatalogItem( const CatalogItem& item, const bool obsolete ) +{ + if( obsolete ) + d->_obsoleteEntries.append(item); + else + d->_entries.append(item); +} + +void CatalogImportPlugin::setCatalogExtraData( const QStringList& data ) +{ + d->_catalogExtraData=data; + d->_updateCatalogExtraData=true; +} + +void CatalogImportPlugin::setGeneratedFromDocbook( const bool generated ) +{ + d->_generatedFromDocbook = generated; + d->_updateGeneratedFromDocbook = true; +} + +void CatalogImportPlugin::setErrorIndex(const QValueList<uint>& errors) +{ + d->_errorList = errors; + d->_updateErrorList = true; +} + +void CatalogImportPlugin::setFileCodec(QTextCodec* codec) +{ + d->_codec=codec; + d->_updateCodec = true; +} + +void CatalogImportPlugin::setHeader( const CatalogItem& item ) +{ + d->_header=item; + d->_updateHeader=true; +} + +void CatalogImportPlugin::setMimeTypes( const QString& mimetypes ) +{ + d->_mimeTypes=mimetypes; +} + +ConversionStatus CatalogImportPlugin::open(const QString& file, const QString& mimetype, Catalog* catalog) +{ + d->_stopped=false; + d->_catalog=catalog; + startTransaction(); + + ConversionStatus result = load(file, mimetype); + if( d->_stopped ) + { + d->_started=false; + return STOPPED; + } + + if( result == OK || result == RECOVERED_PARSE_ERROR || result == RECOVERED_HEADER_ERROR ) + commitTransaction(); + + return result; +} + +void CatalogImportPlugin::startTransaction() +{ + d->_started = (d->_catalog!=0); + + d->_updateCodec = false; + d->_updateCatalogExtraData = false; + d->_updateGeneratedFromDocbook = false; + d->_updateErrorList = false; + d->_updateHeader = false; + d->_mimeTypes = "text/plain"; + d->_entries.clear(); +} + +void CatalogImportPlugin::commitTransaction() +{ + if( d->_started ) + { + d->_catalog->clear(); + + // fill in the entries + QValueVector<CatalogItem> e; + e.reserve( d->_entries.count() ); + for( QValueList<CatalogItem>::const_iterator it = d->_entries.begin(); it != d->_entries.end(); ++it ) + { + e.append( *it ); + } + d->_catalog->setEntries( e ); + + d->_catalog->setObsoleteEntries( d->_obsoleteEntries ); + + if( d->_updateCodec ) d->_catalog->setFileCodec(d->_codec); + if( d->_updateCatalogExtraData ) + d->_catalog->setCatalogExtraData(d->_catalogExtraData); + if( d->_updateGeneratedFromDocbook ) + d->_catalog->setGeneratedFromDocbook(d->_generatedFromDocbook); + if( d->_updateHeader ) + d->_catalog->setHeader(d->_header); + // generate before setting errors, since it clears the error list + d->_catalog->generateIndexLists(); + if( d->_updateErrorList ) + d->_catalog->setErrorIndex(d->_errorList); + + d->_catalog->setImportPluginID(id()); + d->_catalog->setMimeTypes( d->_mimeTypes ); + } + + d->_started = false; +} + +QStringList CatalogImportPlugin::availableImportMimeTypes() +{ + QStringList result; + + KTrader::OfferList offers = KTrader::self()->query("KBabelFilter", "exist [X-KDE-Import]"); + + for( KTrader::OfferListIterator ptr = offers.begin(); ptr!=offers.end() ; ++ptr ) + { + result += (*ptr)->property("X-KDE-Import").toStringList(); + } + + return result; +} + +bool CatalogImportPlugin::isStopped() const +{ + return d->_stopped; +} + +void CatalogImportPlugin::stop() +{ + d->_stopped = true; +} + +#include "catalogfileplugin.moc" diff --git a/kbabel/common/importplugin_private.h b/kbabel/common/importplugin_private.h new file mode 100644 index 00000000..3c45919c --- /dev/null +++ b/kbabel/common/importplugin_private.h @@ -0,0 +1,69 @@ +/* **************************************************************************** + This file is part of KBabel + + Copyright (C) 2002-2003 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., 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. + +**************************************************************************** */ +#ifndef IMPORTPLUGINPRIVATE_H +#define IMPORTPLUGINPRIVATE_H + +#include "catalogitem.h" + +class QTextCodec; + +namespace KBabel { + +class Catalog; + +class CatalogImportPluginPrivate +{ +public: + Catalog* _catalog; + bool _started; + bool _stopped; + + QValueList<CatalogItem> _entries; + QValueList<CatalogItem> _obsoleteEntries; + CatalogItem _header; + bool _generatedFromDocbook; + QTextCodec* _codec; + QValueList<uint> _errorList; + QStringList _catalogExtraData; + QString _mimeTypes; + + bool _updateHeader; + bool _updateGeneratedFromDocbook; + bool _updateCodec; + bool _updateErrorList; + bool _updateCatalogExtraData; +}; + +} + +#endif diff --git a/kbabel/common/itempart.h b/kbabel/common/itempart.h new file mode 100644 index 00000000..842f195b --- /dev/null +++ b/kbabel/common/itempart.h @@ -0,0 +1,37 @@ +/* **************************************************************************** + This file is part of KBabel + + Copyright (C) 2002-2005 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., 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. +**************************************************************************** */ +#ifndef ITEMPART_H +#define ITEMPART_H + +enum Part {UndefPart, Msgid, Msgstr, Comment}; + +#endif diff --git a/kbabel/common/kbabel-projectrename.upd b/kbabel/common/kbabel-projectrename.upd new file mode 100644 index 00000000..f2e8510b --- /dev/null +++ b/kbabel/common/kbabel-projectrename.upd @@ -0,0 +1,13 @@ +Id=kde34 +File=kbabel.defaultproject +Group=Header +Author-Email,AuthorEmail +Author-Name,AuthorName +Local-Author-Name,LocalAuthorName +Update-Charset,UpdateCharset +Update-Encoding,UpdateEncoding +Update-Language-Team,UpdateLanguageTeam +Update-Last-Translator,UpdateLastTranslator +Update-Project,UpdateProject +Update-Revision,UpdateRevision +#eof diff --git a/kbabel/common/kbabeldatatool.h b/kbabel/common/kbabeldatatool.h new file mode 100644 index 00000000..57d9f771 --- /dev/null +++ b/kbabel/common/kbabeldatatool.h @@ -0,0 +1,63 @@ +/* **************************************************************************** + This file is part of KBabel + + Copyright (C) 2003 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., 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. + +**************************************************************************** */ +#ifndef KBABELDATATOOL_H +#define KBABELDATATOOL_H + +#include <kdatatool.h> + +namespace KBabel +{ + +/** + * This class represents a base class for a KBabel @ref KDataTool. + * It supports automatic settings management. + * + * @short This class represents a base class for a KBabel data tool. + */ +class KBabelDataTool: public KDataTool +{ + Q_OBJECT + +protected: + KBabelDataTool( QObject* parent, const char* name ) : KDataTool( parent, name ) {} +public slots: + /** The only method to be redefined in inherited classes. + * This method is called everytime the settings are changed. + * @param project is ID of the project, for which the settings changed. + */ + virtual void settingsChanged(const QString &project) = 0; +}; + +} + +#endif // KBABELDATATOOL_H diff --git a/kbabel/common/kbabelfilter.desktop b/kbabel/common/kbabelfilter.desktop new file mode 100644 index 00000000..f9573ad2 --- /dev/null +++ b/kbabel/common/kbabelfilter.desktop @@ -0,0 +1,60 @@ +[Desktop Entry] +Type=ServiceType +X-KDE-ServiceType=KBabelFilter +Comment=KBabel filter +Comment[bg]=Филтър - KBabel +Comment[br]=Sil KBabel +Comment[ca]=Filtre de KBabel +Comment[cs]=KBabel filtr +Comment[cy]=Hidlydd KBabel +Comment[da]=KBabel-filter +Comment[de]=KBabel-Filter +Comment[el]=ΦίλτÏο KBabel +Comment[es]=Filtro de KBabel +Comment[et]=KBabeli filter +Comment[eu]=KBabel iragazkia +Comment[fa]=پالایۀ KBabel +Comment[fi]=KBabel-suodatin +Comment[fr]=Filtre pour KBabel +Comment[ga]=Scagaire KBabel +Comment[gl]=Filtro de KBabel +Comment[he]=×ž×¡× ×Ÿ KBabel +Comment[hi]=के-बेबल फ़िलà¥à¤Ÿà¤° +Comment[hu]=KBabel-szűrÅ‘ +Comment[is]=KBabel sÃa +Comment[it]=Filtro KBabel +Comment[ja]=KBabel フィルタ +Comment[ka]=KBabel ფილტრი +Comment[kk]=KBabel ÑүзгіÑÑ– +Comment[lt]=KBabel filtras +Comment[ms]=Penapis KBabel +Comment[nb]=KBabel-filter +Comment[nds]=KBabel-Filter +Comment[ne]=केबà¥à¤¯à¤¾à¤¬à¤² फिलà¥à¤Ÿà¤° +Comment[nl]=KBabel-filter +Comment[nn]=KBabel-filter +Comment[pa]=KBabel ਫਿਲਟਰ +Comment[pl]=Filtr KBabel +Comment[pt]=Filtro do KBabel +Comment[pt_BR]=Filtro KBabel +Comment[ru]=Фильтр KBabel +Comment[sk]=Filter pre KBabel +Comment[sl]=Filter KBabel +Comment[sr]=KBabel-ов филтер +Comment[sr@Latn]=KBabel-ov filter +Comment[sv]=Kbabel-filter +Comment[ta]=Kபாபேல௠வடிகடà¯à®Ÿà®¿ +Comment[tg]=Филтри KBabel +Comment[tr]=KBabel süzgeci +Comment[uk]=Фільтр KBabel +Comment[zh_CN]=KBabel 过滤器 +Comment[zh_TW]=KBabel éŽæ¿¾å™¨ + +[PropertyDef::X-KDE-Import] +Type=QStringList + +[PropertyDef::X-KDE-Export] +Type=QStringList + +[PropertyDef::X-KDE-Available] +Type=QString diff --git a/kbabel/common/kbmailer.cpp b/kbabel/common/kbmailer.cpp new file mode 100644 index 00000000..fb3ab3b1 --- /dev/null +++ b/kbabel/common/kbmailer.cpp @@ -0,0 +1,244 @@ +/* **************************************************************************** + This file is part of KBabel + + Copyright (C) 2002-2003 by Marco Wegner <mail@marcowegner.de> + 2004 by Stanislav Visnovsky <visnovsky@kde.org> + Copyright (C) 2006 by Nicolas GOUTTE <goutte@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the 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 <qfileinfo.h> +#include <qregexp.h> +#include <qstring.h> +#include <qstringlist.h> +#include <qwidget.h> + +#include <kapplication.h> +#include <kio/netaccess.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <ktar.h> +#include <kurl.h> +#include <kdebug.h> +#include <ktempdir.h> +#include <kinputdialog.h> + +#include "kbmailer.h" + +using namespace KBabel; + +KBabelMailer::KBabelMailer( QWidget* parent, Project::Ptr project) + : _project( project ), m_parent( parent ) +{ + m_tempDir.setAutoDelete( true ); + readConfig(); +} + +KBabelMailer::~KBabelMailer() +{ + saveConfig( ); +} + +void KBabelMailer::sendOneFile(const QString& fileName) +{ + if (!singleFileCompression) { + kapp->invokeMailer("", "", "", "", "", "", fileName); + } else { + const QString archive ( createArchive( QStringList( fileName ), QFileInfo( fileName ).baseName() ) ); + if ( !archive.isEmpty() ) { + kapp->invokeMailer("", "", "", "", "", "", archive); + } + } +} + +void KBabelMailer::sendOneFile( const KURL& url) +{ +#if KDE_IS_VERSION( 3, 5, 0) + const KURL localUrl( KIO::NetAccess::mostLocalURL( url, m_parent ) ); +#else + const KURL localUrl( url ); +#endif + if ( localUrl.isLocalFile() ) + { + sendOneFile( localUrl.path() ); + return; + } + + if (!singleFileCompression) { + QString fileName( url.filename() ); + if ( fileName.isEmpty() ) + { + fileName = "attachment"; + } + // ### TODO: the current implementation has the default to possibly overwrite an already existing temporary file + QString tempName( m_tempDir.name() ); + tempName += fileName; + if ( KIO::NetAccess::download( url, tempName, m_parent ) ) + kapp->invokeMailer("", "", "", "", "", "", fileName); + else + { + KMessageBox::error( m_parent, i18n("Error while trying to download file %1.").arg( url.prettyURL() ) ); + } + } + else + { + const QString archive ( createArchive( QStringList( url.url() ), url.filename() ) ); + if ( !archive.isEmpty() ) { + kapp->invokeMailer("", "", "", "", "", "", archive); + } + } +} + +void KBabelMailer::sendFiles(QStringList fileList, const QString& initialName) +{ + const QString archive ( createArchive( fileList, initialName ) ); + if ( !archive.isEmpty() ) { + kapp->invokeMailer("", "", "", "", "", "", archive); + } +} + +QString KBabelMailer::createArchive(QStringList fileList, QString initialName) +{ + if ( m_tempDir.name().isEmpty() ) + { + kdWarning() << "KBabelMailer has not a valid temporary directory!" << endl; + return QString(); // No temporary directory! + } + + // do nothing if there are no files in the list + if (fileList.empty()) + return QString::null; + + // determine the name of the archive, do nothing if none is given + // or Cancel was pressed + initialName = ( initialName.isEmpty() ? QString("translations") : initialName ); + bool ok = false; + QStringList list( archiveList ); + list.prepend( initialName ); + QString archiveName ( KInputDialog::getItem( i18n("Save"), + i18n( "Enter the name of the archive without file extension" ), + archiveList, 0, true, &ok, m_parent ) ); + if ( !ok || archiveName.isEmpty() ) + return QString(); + + // file extensions are determined from the type of compression + archiveName.remove( QRegExp( "\\.tar\\.(gz|bz2)$" ) ); + + // Update the list of archive names, keep only the ten most recent ones. + archiveList.remove( archiveName ); + archiveList.prepend( archiveName ); + if ( archiveList.count( ) > 10 ) + archiveList.pop_back(); + + // set the correct extension and mimetype + QString mimetype; + if (bzipCompression) { + archiveName += ".tar.bz2"; + mimetype = "application/x-bzip2"; + } else { + archiveName += ".tar.gz"; + mimetype = "application/x-gzip"; + } + + return buildArchive (fileList, m_tempDir.name()+archiveName, mimetype, true); +} + +QString KBabelMailer::buildArchive(QStringList fileList, QString archiveName, QString mimetype, bool remove) +{ + Q_UNUSED( remove ); + // create a new archive + KTar archive(archiveName, mimetype); + if (!archive.open(IO_WriteOnly)) { + KMessageBox::error( m_parent, i18n("Error while trying to create archive file.") ); + return QString::null; + } + + // add files to this archive + QStringList::const_iterator it; + for (it = fileList.constBegin(); it != fileList.constEnd(); ++it) { +#if KDE_IS_VERSION( 3, 5, 0 ) + // Try to get a local URL instead of a remote one + const KURL url( KIO::NetAccess::mostLocalURL( KURL::fromPathOrURL( *it ), m_parent ) ); +#else + const KURL url( KURL::fromPathOrURL( *it ) ); +#endif + QString poTempName; + if ( !KIO::NetAccess::download( url, poTempName, m_parent ) ) { + KMessageBox::error( m_parent, i18n("Error while trying to read file %1.").arg( url.prettyURL() ) ); + continue; + } + + // The files in the archive are stored relatively to the PO base dir + // but only if "PoBaseDir" in the config file is set and the files + // actually reside in one of its subdirectories. Else they are stored + // without relative path. + QString poArchFileName = url.path(); + if (_poBaseDir.isEmpty( ) || poArchFileName.find(_poBaseDir) != 0) + poArchFileName = QFileInfo( poArchFileName ).fileName(); + else + poArchFileName.remove( QRegExp( "^" + QRegExp::escape( _poBaseDir ) + "/?" ) ); + if ( !archive.addLocalFile( poTempName, poArchFileName ) ) + { + KMessageBox::error( m_parent, i18n("Error while trying to copy file %1 into archive.").arg( url.prettyURL() ) ); + } + + KIO::NetAccess::removeTempFile(poTempName); + } + archive.close(); + + return archive.fileName(); +} + +void KBabelMailer::readConfig() +{ + // The relevant variables are never stored in catalogmanagerrc but in + // project config file. Therefore they are read from the project. + + MiscSettings _settings = _project->miscSettings(); + + bzipCompression = _settings.useBzip; + singleFileCompression = _settings.compressSingleFile; + + KConfig* conf = _project->config(); + + conf->setGroup ("Misc"); + archiveList = conf->readListEntry("MailArchiveNames"); + + _poBaseDir = _project->catManSettings().poBaseDir; +} + +void KBabelMailer::saveConfig() +{ + // For an explanation see readConfig( ) + KConfig* _config = _project->config(); + + _config->setGroup("Misc"); + _config->writeEntry("MailArchiveNames", archiveList); +} + diff --git a/kbabel/common/kbmailer.h b/kbabel/common/kbmailer.h new file mode 100644 index 00000000..7df5ce9d --- /dev/null +++ b/kbabel/common/kbmailer.h @@ -0,0 +1,166 @@ +/* **************************************************************************** + This file is part of KBabel + + Copyright (C) 2002-2003 by Marco Wegner <mail@marcowegner.de> + 2004 by Stanislav Visnovsky <visnovsky@kde.org> + Copyright (C) 2006 by Nicolas GOUTTE <goutte@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the 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. + +**************************************************************************** */ + + +#ifndef KBMAILER_H +#define KBMAILER_H + +#include <kbproject.h> +#include <kdemacros.h> +#include <ktempdir.h> + +class QWidget; + +class KURL; + +namespace KBabel +{ + +/** + * Utility class for providing the capability to send compressed mail + * archives from within KBabel and the Catalog Manager. + * + * @short Utility class for compressed mail attachments. + * @author Marco Wegner <mail@marcowegner.de> + * + * @internal + */ +class KDE_EXPORT KBabelMailer +{ + public: + /** + * Constructor. + * + * @param parent The parent widget. + * @param project The project to be used. + * @since 1.11.2 (KDE 3.5.2): @p parent parameter + */ + KBabelMailer( QWidget* parent, Project::Ptr project); + virtual ~KBabelMailer(); + + /** + * Send only one file as a mail attachment. The file can either be sent + * as a compressed or an uncompressed file. + * + * @param fileName the name of the file to be sent. + */ + void sendOneFile(const QString& fileName); + /** + * Send only one file as a mail attachment. The file can either be sent + * as a compressed or an uncompressed file. + * + * @param url the URL of the file to be sent. + * @since 1.11.2 (KDE 3.5.2) + */ + void sendOneFile(const KURL& url); + /** + * Send several files as a mail attachment. The files will be included in + * an archive. + * + * @param fileList contains the names of the files to be sent. + * @param initialName the possible name of the archive + */ + void sendFiles(QStringList fileList, const QString& initialName = QString::null); + /** + * Sets the the PO files' base directory. + * + * @param dir the PO file base dir. + */ + void setPOBaseDir(const QString& dir) { _poBaseDir = dir; } + + + /** + * This is where the archive is actually created. + * + * @param fileList contains the names of the files to be included in + * the archive. + * @param initialName the name used as initial name when the archive + * name is queried. + * + * @return name of the archive if it was created successfully, + * otherwise QString::null. + */ + QString createArchive(QStringList fileList, QString initialName); + + /** + * Write the archive file. + */ + QString buildArchive(QStringList fileList, QString fileName, QString mimetype, bool remove = true); + private: + /** + * Read the config file to extract the values for compression + * method and compression of single files. + */ + void readConfig(); + /** + * Save the config settings, only the list of recent archive names for now. + */ + void saveConfig( ); + + + private: + /** + * Temporary directory + * @since 1.11.2 (KDE 3.5.2) + */ + KTempDir m_tempDir; + + /** + * Whether to use bzip2 as compression method. If false use gzip. + */ + bool bzipCompression; + /** + * Whether to compress single files as well. + */ + bool singleFileCompression; + /** + * This QStringList stores the recently used archive names. + */ + QStringList archiveList; + /** + * The project object. + */ + Project::Ptr _project; + /** + * The path to the PO Base directory + */ + QString _poBaseDir; + + /// The parent widget + QWidget* m_parent; +}; + +} + +#endif // KBMAILER_H diff --git a/kbabel/common/kbproject.cpp b/kbabel/common/kbproject.cpp new file mode 100644 index 00000000..38996b51 --- /dev/null +++ b/kbabel/common/kbproject.cpp @@ -0,0 +1,477 @@ +/* **************************************************************************** + This file is part of KBabel + + Copyright (C) 2004 by Stanislav Visnovsky <visnovsky@kde.org> + Copyright (C) 2006 by Nicolas GOUTTE <goutte@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the 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 "resources.h" +#include "kbproject.h" +#include "kbprojectmanager.h" +#include "kbprojectsettings.h" + +#include <qfileinfo.h> + +#include <kdebug.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kmimetype.h> +#include <kspell.h> + +namespace KBabel +{ + +Project::Project( const QString& file ) : + QObject () + , _filename (file) + , _name (i18n("unnamed")) + , _valid (false) + , _config (NULL) + , _settings (NULL) +{ + QFileInfo info(file); + + if(info.isDir()) + return; + + if(info.exists()) + { + // ### TODO: can a setting file only be a text/plain file? + // ### TODO: why not use KMimeType::findByPath if the fileis always local? + // first, for existing file check the MIME type + // it has to be text file + KMimeType::Ptr mime = KMimeType::findByURL( KURL::fromPathOrURL( file ) ); + + if ( mime->name() == "text/plain" ) + { + // Plain text, no problem! + } + else if ( mime->name() == "application/x-zerosize" ) + { + // Empty files are allowed too + kdWarning() << "Empty KBabel project file!" << endl; + } + else if ( mime->name() == "application/octet-stream" ) + { + // this can happen if the file has some very long lines + kdWarning() << "KBabel project file detected as octet-stream! Perhaps there are very long lines in it!" << endl; + } + else + { + // Unknown mime type, abort! + // ### TODO: should the user not be asked instead? + kdWarning () << "File type is " << mime->name() << endl; + return; + } + } + + _config = KSharedConfig::openConfig (_filename, false, false); + kdDebug (KBABEL) << "Opened project " << _filename << endl; + + // read the project name + _config->setGroup( "Project" ); + _name = _config->readEntry( "Name", QString() ); + if ( _name.isEmpty() ) + _name = "Default-Project"; // set default project name + + // ### FIXME: why is the Version number not written to the project file? + if ( _config->readEntry( "Version", QString() ) != "1.0.1" ) + { + kdWarning() << "Old project format assumed" << endl; + + // transform old entries to the new ones + _config->setGroup( "Header"); + + // TODO remove obsolete entries as well + if (_config->hasKey("Author-Email") && !_config->hasKey("AuthorEmail")) + { + _config->writeEntry ("AuthorEmail", _config->readEntry ("Author-Email", "")); + _config->deleteEntry ("Author-Email"); + } + + if (_config->hasKey("Author-Name") && !_config->hasKey("AuthorName")) + { + _config->writeEntry ("AuthorName", _config->readEntry ("Author-Name", "")); + _config->deleteEntry ("Author-Name"); + } + + if (_config->hasKey("Local-Author-Name") && !_config->hasKey("LocalAuthorName")) + { + _config->writeEntry ("LocalAuthorName", _config->readEntry ("Local-Author-Name", "")); + _config->deleteEntry ("Local-Author-Name"); + } + + if (_config->hasKey("Update-Charset") && !_config->hasKey("UpdateCharset")) + { + _config->writeEntry ("UpdateCharset", _config->readEntry ("Update-Charset", "")); + _config->deleteEntry ("Update-Charset"); + } + + if (_config->hasKey("Update-Encoding") && !_config->hasKey("UpdateEncoding")) + { + _config->writeEntry ("UpdateEncoding", _config->readEntry ("Update-Encoding", "")); + _config->deleteEntry ("Update-Encoding"); + } + + if (_config->hasKey("Update-Language-Team") && !_config->hasKey("UpdateLanguageTeam")) + { + _config->writeEntry ("UpdateLanguageTeam", _config->readEntry ("Update-Language-Team", "")); + _config->deleteEntry ("Update-Language-Team"); + } + + if (_config->hasKey("Update-Last-Translator") && !_config->hasKey("UpdateLastTranslator")) + { + _config->writeEntry ("UpdateLastTranslator", _config->readEntry ("Update-Last-Translator", "")); + _config->deleteEntry ("Update-Last-Translator"); + } + + if (_config->hasKey("Update-Project") && !_config->hasKey("UpdateProject")) + { + _config->writeEntry ("UpdateProject", _config->readEntry ("Update-Project", "")); + _config->deleteEntry ("Update-Project"); + } + + if (_config->hasKey("Update-Revision") && !_config->hasKey("UpdateRevision")) + { + _config->writeEntry ("UpdateRevision", _config->readEntry ("Update-Revision", "")); + _config->deleteEntry ("Update-Revision"); + } + + _config->sync(); + } + + _valid = true; + + _settings = new KBabel::ProjectSettingsBase( _config ); + _settings->readConfig(); +} + +Project::~Project () +{ + if (_settings) + { + // store the project name + _settings->setVersion( "1.0.1" ); + _settings->setName(_name); + + kdDebug () << "Writing configuration" << endl; + _settings->writeConfig(); + + delete _settings; + } + + // unregister in project manager + kdDebug () << "Freeing project " << _filename << endl; + ProjectManager::remove (this); +} + +KConfig* Project::config () +{ + return _config; +} + +KSharedConfig* Project::sharedConfig( void ) +{ + return _config; +} + +ProjectSettingsBase* Project::settings () +{ + return _settings; +} + +IdentitySettings Project::identitySettings () +{ + IdentitySettings settings; + + settings.authorName=_settings->authorName(); + settings.authorLocalizedName=_settings->localAuthorName(); + settings.authorEmail=_settings->authorEmail(); + settings.languageName=_settings->language(); + settings.languageCode=_settings->languageCode(); + settings.mailingList=_settings->mailinglist(); + settings.timeZone=_settings->timezone(); + + settings.numberOfPluralForms=_settings->pluralForms(); + + if( settings.numberOfPluralForms < -1 ) + { + kdWarning() << "Invalid number of plural forms, ignoring: " << settings.numberOfPluralForms << endl; + settings.numberOfPluralForms = 2; + } + settings.checkPluralArgument=_settings->checkPluralArgument(); + settings.gnuPluralFormHeader=_settings->pluralFormsHeader(); + + return settings; +} + +SaveSettings Project::saveSettings () +{ + SaveSettings settings; + + settings.autoUpdate=_settings->autoUpdate(); + settings.updateLastTranslator=_settings->updateLastTranslator(); + settings.updateRevisionDate=_settings->updateRevisionDate(); + settings.updateLanguageTeam=_settings->updateLanguageTeam(); + settings.updateCharset=_settings->updateCharset(); + settings.updateEncoding=_settings->updateEncoding(); + settings.encoding=_settings->encoding(); + settings.useOldEncoding=_settings->useOldEncoding(); + + settings.updateProject=_settings->updateProject(); + settings.projectString=_settings->projectString(); + + settings.autoSyntaxCheck = _settings->autoSyntaxCheck(); + settings.saveObsolete = _settings->saveObsolete(); + settings.customDateFormat = _settings->customDateFormat(); + settings.dateFormat = (Qt::DateFormat)_settings->dateFormat(); + settings.updateDescription = _settings->updateDescription(); + settings.descriptionString = _settings->descriptionString(); + settings.updateTranslatorCopyright = _settings->updateTranslatorCopyright(); + settings.FSFCopyright = _settings->fSFCopyright(); + + settings.autoSaveDelay=_settings->autoSaveDelay(); + + return settings; +} + + +MiscSettings Project::miscSettings () +{ + MiscSettings settings; + + QString temp=_settings->accelMarker(); + if(temp.length() > 0) + { + settings.accelMarker=temp[0]; + } + + temp = _settings->contextInfo(); + + settings.contextInfo.setPattern(temp); + + temp = _settings->singularPlural(); + settings.singularPlural.setPattern(temp); + + settings.useBzip = _settings->bZipCompression(); + settings.compressSingleFile = _settings->compressSingleFile(); + + return settings; +} + +SpellcheckSettings Project::spellcheckSettings () +{ + SpellcheckSettings settings; + + settings.noRootAffix=_settings->noRootAffix(); + settings.runTogether=_settings->runTogether(); + settings.spellEncoding=_settings->spellEncoding(); + settings.spellClient=_settings->spellClient(); + settings.spellDict=_settings->spellDictionary(); + settings.rememberIgnored=_settings->rememberIgnored(); + settings.ignoreURL=_settings->ignoreURL(); + settings.onFlySpellcheck=_settings->onFlySpellCheck(); + + settings.valid=true; + + return settings; +} + +SourceContextSettings Project::sourceContextSettings () +{ + SourceContextSettings settings; + + settings.codeRoot=_settings->codeRoot(); + settings.sourcePaths=_settings->paths(); + + return settings; +} + +CatManSettings Project::catManSettings () +{ + CatManSettings settings; + + settings.poBaseDir=_settings->poBaseDir(); + settings.potBaseDir=_settings->potBaseDir(); + settings.openWindow=_settings->openWindow(); + + settings.killCmdOnExit=_settings->killCmdOnExit(); + settings.indexWords=_settings->indexWords(); + settings.msgfmt=_settings->msgfmt(); + + + settings.dirCommands = _settings->dirCommands(); + settings.dirCommandNames = _settings->dirCommandNames(); + + settings.fileCommands = _settings->fileCommands(); + settings.fileCommandNames = _settings->fileCommandNames(); + + settings.ignoreURL=_settings->ignoreURL(); + + settings.flagColumn=_settings->showFlagColumn(); + settings.fuzzyColumn=_settings->showFuzzyColumn(); + settings.untranslatedColumn=_settings->showUntranslatedColumn(); + settings.totalColumn=_settings->showTotalColumn(); + settings.cvsColumn=_settings->showCVSColumn(); + settings.revisionColumn=_settings->showRevisionColumn(); + settings.translatorColumn=_settings->showTranslatorColumn(); + + return settings; +} + +void Project::setSettings (IdentitySettings settings) +{ + _settings->setAuthorName(settings.authorName); + _settings->setLocalAuthorName(settings.authorLocalizedName); + _settings->setAuthorEmail(settings.authorEmail); + _settings->setAuthorEmail(settings.authorEmail); + _settings->setLanguage(settings.languageName); + _settings->setLanguageCode(settings.languageCode); + _settings->setMailinglist(settings.mailingList); + _settings->setTimezone(settings.timeZone); + _settings->setPluralForms(settings.numberOfPluralForms); + _settings->setCheckPluralArgument(settings.checkPluralArgument); + _settings->setPluralFormsHeader(settings.gnuPluralFormHeader); + + _settings->writeConfig(); + + emit signalIdentitySettingsChanged(); + emit signalSettingsChanged(); +} + +void Project::setSettings (SaveSettings settings) +{ + _settings->setAutoUpdate(settings.autoUpdate); + _settings->setUpdateLastTranslator(settings.updateLastTranslator); + _settings->setUpdateRevisionDate(settings.updateRevisionDate); + _settings->setUpdateLanguageTeam(settings.updateLanguageTeam); + _settings->setUpdateCharset(settings.updateCharset); + _settings->setUpdateEncoding(settings.updateEncoding); + _settings->setEncoding(settings.encoding); + _settings->setUseOldEncoding(settings.useOldEncoding); + + _settings->setUpdateProject(settings.updateProject); + _settings->setProjectString(settings.projectString); + + _settings->setAutoSyntaxCheck(settings.autoSyntaxCheck); + _settings->setSaveObsolete(settings.saveObsolete); + _settings->setCustomDateFormat(settings.customDateFormat); + _settings->setDateFormat(settings.dateFormat); + _settings->setUpdateDescription(settings.updateDescription); + _settings->setDescriptionString(settings.descriptionString); + _settings->setUpdateTranslatorCopyright(settings.updateTranslatorCopyright); + _settings->setFSFCopyright(settings.FSFCopyright); + + _settings->setAutoSaveDelay(settings.autoSaveDelay); + + _settings->writeConfig(); + + emit signalSaveSettingsChanged(); + emit signalSettingsChanged(); +} + + +void Project::setSettings (MiscSettings settings) +{ + _settings->setAccelMarker(settings.accelMarker); + _settings->setContextInfo(settings.contextInfo.pattern()); + _settings->setSingularPlural(settings.singularPlural.pattern()); + _settings->setBZipCompression(settings.useBzip); + _settings->setCompressSingleFile(settings.compressSingleFile); + + _settings->writeConfig(); + + emit signalMiscSettingsChanged(); + emit signalSettingsChanged(); +} + +void Project::setSettings (SpellcheckSettings settings) +{ + _settings->setNoRootAffix(settings.noRootAffix); + _settings->setRunTogether(settings.runTogether); + _settings->setSpellEncoding(settings.spellEncoding); + _settings->setSpellClient(settings.spellClient); + _settings->setSpellDictionary(settings.spellDict); + _settings->setRememberIgnored(settings.rememberIgnored); + _settings->setIgnoreURL(settings.ignoreURL); + _settings->setOnFlySpellCheck(settings.onFlySpellcheck); + + _settings->writeConfig(); + + emit signalSpellcheckSettingsChanged(); + emit signalSettingsChanged(); +} + +void Project::setSettings (SourceContextSettings settings) +{ + KConfigGroupSaver saver(_config,"SourceContext"); + + _settings->setCodeRoot(settings.codeRoot); + _settings->setPaths(settings.sourcePaths); + + _settings->writeConfig(); + + emit signalSourceContextSettingsChanged(); + emit signalSettingsChanged(); +} + +void Project::setSettings (CatManSettings settings) +{ + _settings->setPoBaseDir(settings.poBaseDir); + _settings->setPotBaseDir(settings.potBaseDir); + _settings->setOpenWindow(settings.openWindow); + + _settings->setKillCmdOnExit(settings.killCmdOnExit); + _settings->setIndexWords(settings.indexWords); + + _settings->setDirCommands(settings.dirCommands); + _settings->setDirCommandNames(settings.dirCommandNames); + + _settings->setFileCommands(settings.fileCommands); + _settings->setFileCommandNames(settings.fileCommandNames); + + _settings->setValidationIgnoreURL(settings.ignoreURL); + + _settings->setShowFlagColumn(settings.flagColumn); + _settings->setShowFuzzyColumn(settings.fuzzyColumn); + _settings->setShowUntranslatedColumn(settings.untranslatedColumn); + _settings->setShowTotalColumn(settings.totalColumn); + _settings->setShowCVSColumn(settings.cvsColumn); + _settings->setShowRevisionColumn(settings.revisionColumn); + _settings->setShowTranslatorColumn(settings.translatorColumn); + + _settings->writeConfig(); + + emit signalCatManSettingsChanged(); + emit signalSettingsChanged(); +} + +} + +#include "kbproject.moc" diff --git a/kbabel/common/kbproject.h b/kbabel/common/kbproject.h new file mode 100644 index 00000000..1e1ebc79 --- /dev/null +++ b/kbabel/common/kbproject.h @@ -0,0 +1,109 @@ +/* **************************************************************************** + This file is part of KBabel + + Copyright (C) 2004 by Stanislav Visnovsky <visnovsky@kde.org> + Copyright (C) 2006 by Nicolas GOUTTE <goutte@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the 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. + +**************************************************************************** */ +#ifndef KBPROJECT_H +#define KBPROJECT_H + +#include <qstring.h> + +#include <kconfig.h> +#include "catalogsettings.h" +#include "projectsettings.h" + +namespace KBabel +{ + class ProjectSettingsBase; + + class KDE_EXPORT Project : public QObject, public KShared + { + Q_OBJECT + public: + typedef KSharedPtr <Project> Ptr; + + Project( const QString& file ); + virtual ~Project (); + + QString filename () const { return _filename; } + QString name () const { return _name; } + + void setName( const QString& name ) { _name = name; } + + KConfig* config (); + /** + * Returns the KSharedConfig pointer of the project data + * @since 1.11.2 (KDE 3.5.2) + */ + KSharedConfig* sharedConfig( void ); + + ProjectSettingsBase* settings (); + + bool valid () { return _valid; } + + IdentitySettings identitySettings (); + SaveSettings saveSettings (); + MiscSettings miscSettings (); + SpellcheckSettings spellcheckSettings (); + SourceContextSettings sourceContextSettings (); + CatManSettings catManSettings (); + + void setSettings(KBabel::CatManSettings newSettings); + void setSettings(KBabel::SaveSettings newSettings); + void setSettings(KBabel::IdentitySettings newSettings); + void setSettings(KBabel::MiscSettings newSettings); + void setSettings(KBabel::SpellcheckSettings newSettings); + void setSettings(KBabel::SourceContextSettings newSettings); + + signals: + void signalIdentitySettingsChanged(); + void signalSaveSettingsChanged(); + void signalMiscSettingsChanged(); + void signalSpellcheckSettingsChanged(); + void signalSourceContextSettingsChanged(); + void signalCatManSettingsChanged(); + + /** + * This is general purpose signal emitted additionally + * to the signals above. + */ + void signalSettingsChanged(); + + private: + QString _filename; + QString _name; + bool _valid; + KSharedConfig::Ptr _config; + ProjectSettingsBase* _settings; + }; + +} + +#endif // KBPROJECT_H diff --git a/kbabel/common/kbprojectmanager.cpp b/kbabel/common/kbprojectmanager.cpp new file mode 100644 index 00000000..048c530a --- /dev/null +++ b/kbabel/common/kbprojectmanager.cpp @@ -0,0 +1,101 @@ +/* **************************************************************************** + This file is part of KBabel + + Copyright (C) 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., 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 <kdebug.h> +#include <kstandarddirs.h> + +#include "kbprojectmanager.h" + +namespace KBabel +{ + +QPtrList<Project> ProjectManager::p_list; +QString ProjectManager::strDefaultProjectName = QString(); + +Project::Ptr ProjectManager::open( const QString& file ) +{ + kdDebug() << k_funcinfo << " " << file << endl; + // TODO: ensure full path + // TODO: isn't a map better? + for(QPtrList<Project>::ConstIterator it = p_list.constBegin(); + it != p_list.constEnd(); ++it) + { + if ((*it)->filename() == file) + { + kdDebug() << k_funcinfo << " returning existing project " << (void*) it << endl; + return (*it); + } + } + + Project::Ptr f = new Project (file); + + if( ! f->valid() ) + { + kdWarning() << "New invalid project for " << file << endl; + return NULL; + } + + kdDebug() << k_funcinfo << " creating new project " << (void*) f << endl; + p_list.append (f); + return f; +} + +void ProjectManager::close( Project::Ptr project) +{ + // this does nothing, we don't really close projects ATM, just sync the configuration + kdDebug() << k_funcinfo << " closing project " << (void*) project << " count: " << project.count() << endl; + project->config()->sync(); +} + +QString ProjectManager::defaultProjectName( void ) +{ + if ( strDefaultProjectName.isEmpty() ) + strDefaultProjectName = locateLocal("config", "kbabel.defaultproject" ); + return strDefaultProjectName; +} + +Project::Ptr ProjectManager::create() +{ + kdWarning() << k_funcinfo << " was called!" << endl; + // TODO: + return 0; +} + +void ProjectManager::remove( Project* ref ) +{ + kdDebug() << k_funcinfo << " Final remove of project " << (void*) ref << " count remaining: " << ref->_KShared_count() << endl; + p_list.remove (ref); +} + +} + diff --git a/kbabel/common/kbprojectmanager.h b/kbabel/common/kbprojectmanager.h new file mode 100644 index 00000000..418183bb --- /dev/null +++ b/kbabel/common/kbprojectmanager.h @@ -0,0 +1,62 @@ +/* **************************************************************************** + This file is part of KBabel + + Copyright (C) 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., 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. + +**************************************************************************** */ +#ifndef KBPROJECTMANAGER_H +#define KBPROJECTMANAGER_H + +#include <qstring.h> +#include <qptrlist.h> +#include <kdemacros.h> + +#include "kbproject.h" + +namespace KBabel +{ + + class KDE_EXPORT ProjectManager + { + public: + static Project::Ptr open( const QString& file ); + static void close( Project::Ptr project ); + static Project::Ptr create(); + static QString defaultProjectName( void ); + + friend class Project; + private: + static void remove (Project*); + static QPtrList<Project> p_list; + static QString strDefaultProjectName; + }; + +} + +#endif // KBPROJECTMANAGER_H diff --git a/kbabel/common/kbprojectsettings.kcfg b/kbabel/common/kbprojectsettings.kcfg new file mode 100644 index 00000000..784189ec --- /dev/null +++ b/kbabel/common/kbprojectsettings.kcfg @@ -0,0 +1,354 @@ +<?xml version="1.0" encoding="UTF-8"?> +<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0 + http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" > + <kcfgfile arg="true"/> + <include>catalogsettings.h</include> + <include>projectsettings.h</include> + + <entry name="ValidateIgnoreFuzzy" type="Bool"> + <label>If the validation tools should ignore fuzzy translations</label> + <default>false</default> + </entry> + <entry name="ValidateMarkAsFuzzy" type="Bool"> + <label>If the validation tools should mark error entries as fuzzy</label> + <default>false</default> + </entry> + <group name="CatalogManager"> + <entry name="DirCommandNames" type="StringList"> + <label>List of command names for directories</label> + <default>Make,Make install,CVS Update</default> + </entry> + <entry name="DirCommands" type="StringList"> + <label>List of commands for directories</label> + <default>make,make install,cvs update</default> + </entry> + <entry name="FileCommandNames" type="StringList"> + <label>List of command names for files</label> + <default>Start application,Compile,CVS Conflict Resolution</default> + </entry> + <entry name="FileCommands" type="StringList"> + <label>List of commands for files</label> + <default>@PACKAGE@,msgfmt -o @PACKAGE@.gmo @PACKAGE@.po,cervisia @PODIR@ --resolve @PACKAGE@.po</default> + </entry> + <entry name="IndexWords" type="Bool"> + <label>If the file cache should contain also index of words for faster searching</label> + <default>false</default> + </entry> + <entry name="KillCmdOnExit" type="Bool"> + <label>If the Catalog Manager should kill all running its gettext tools at exit</label> + <default>true</default> + </entry> + <entry name="msgfmt" type="Bool"> + <label></label> + <default>true</default> + </entry> + <entry name="Marker" type="StringList"> + <label>List of files marked</label> + </entry> + <entry name="OpenWindow" type="Bool"> + <label>If the files should be open in new KBabel editor windows</label> + <default>false</default> + </entry> + <entry name="PoBaseDir" type="String"> + <label>The base directory for PO files (translations)</label> + <default></default> + </entry> + <entry name="PotBaseDir" type="String"> + <label>The base directory for POT files (templates to be translated)</label> + <default></default> + </entry> + <entry name="ShowTotalColumn" type="Bool"> + <label> + </label> + <default>true</default> + </entry> + <entry name="ShowCVSColumn" type="Bool"> + <label> + </label> + <default>true</default> + </entry> + <entry name="ShowFlagColumn" type="Bool"> + <label> + </label> + <default>true</default> + </entry> + <entry name="ShowFuzzyColumn" type="Bool"> + <label> + </label> + <default>true</default> + </entry> + <entry name="ShowRevisionColumn" type="Bool"> + <label> + </label> + <default>true</default> + </entry> + <entry name="ShowTranslatorColumn" type="Bool"> + <label> + </label> + <default>true</default> + </entry> + <entry name="ShowUntranslatedColumn" type="Bool"> + <label> + </label> + <default>true</default> + </entry> + <entry name="ValidationIgnoreURL" type="String"> + <label> + </label> + <default code="true">KBabel::Defaults::CatalogManager::ignoreURL()</default> + </entry> + </group> + <group name="Header"> + <entry name="AuthorEmail" type="String"> + <label>Email of the translator</label> + <default code="true">Defaults::Identity::authorEmail()</default> + </entry> + <entry name="AuthorName" type="String"> + <label>Name of the translator (non-localized)</label> + <default code="true">Defaults::Identity::authorName()</default> + </entry> + <entry name="AutoSaveDelay" type="UInt"> + <label>Delay in minutes between autosaves. 0 disables autosave.</label> + <default>0</default> + </entry> + <entry name="AutoSyntaxCheck" type="Bool"> + <label>If the syntax should be checked before save</label> + <default>true</default> + </entry> + <entry name="AutoUpdate" type="Bool"> + <label>If the header should be automatically updated on save</label> + <default>true</default> + </entry> + <entry name="CheckPluralArgument" type="Bool"> + <label>If the plural argument is required to be a part of translation</label> + <default>true</default> + </entry> + <entry name="CustomDateFormat" type="String"> + <label>Custom date format used if DateFormat specifies that</label> + <default>%Y-%m-%d %H:%M%z</default> + </entry> + <entry name="DateFormat" type="Enum"> + <label>Format of the dates stored</label> + <choices> + <choice name="Custom"/> + <choice name="Default"/> + <choice name="Local"/> + </choices> + <default>Default</default> + </entry> + <entry name="DescriptionString" type="String"> + <label>Description of the translation</label> + <default>translation of @PACKAGE@.po to @LANGUAGE@</default> + </entry> + <entry name="Encoding" type="Enum"> + <label>The encoding of the file</label> + <choices> + <choice name="Locale"/> + <choice name="UTF8"/> + <choice name="UTF16"/> + </choices> + <default>UTF8</default> + </entry> + <entry name="FSFCopyright" type="Enum"> + <label>The way how to handle Free Software Foundation header</label> + <choices> + <choice name="Remove"/> + <choice name="Update"/> + <choice name="NoChange"/> + <choice name="RemoveLine"/> + </choices> + <default>Update</default> + </entry> + <entry name="Language" type="String"> + <label>English name of the language</label> + <default></default> + </entry> + <entry name="LanguageCode" type="String"> + <label>ISO 631 language code</label> + <default code="true">Defaults::Identity::languageCode()</default> + </entry> + <entry name="LocalAuthorName" type="String"> + <label>Localized name of the author</label> + <default></default> + </entry> + <entry name="Mailinglist" type="String"> + <label>Mailing list for i18n of the langauge</label> + <default code="true">Defaults::Identity::mailingList()</default> + </entry> + <entry name="PluralForms" type="UInt"> + <label>Number of plural forms for the language</label> + <default>2</default> + </entry> + <entry name="PluralFormsHeader" type="String"> + <label>Plural forms specification for GNU gettext</label> + <default></default> + </entry> + <entry name="ProjectString" type="String"> + <label>Macro-based string to fill Project GNU header</label> + <default>@PACKAGE@</default> + </entry> + <entry name="SaveObsolete" type="Bool"> + <label>If the obsolete translation entries should be saved</label> + <default>false</default> + </entry> + <entry name="Timezone" type="String"> + <label>Timezone of the translation (needed for updating time stamps)</label> + <default code="true">Defaults::Identity::timezone()</default> + </entry> + <entry name="UpdateCharset" type="Bool"> + <label> + </label> + <default>true</default> + </entry> + <entry name="UpdateEncoding" type="Bool"> + <label> + </label> + <default>true</default> + </entry> + <entry name="UpdateLanguageTeam" type="Bool"> + <label> + </label> + <default>true</default> + </entry> + <entry name="UpdateLastTranslator" type="Bool"> + <label> + </label> + <default>true</default> + </entry> + <entry name="UpdateProject" type="Bool"> + <label> + </label> + <default>true</default> + </entry> + <entry name="UpdateRevisionDate" type="Bool"> + <label> + </label> + <default>true</default> + </entry> + <entry name="UpdateDescription" type="Bool"> + <label> + </label> + <default>true</default> + </entry> + <entry name="UpdateTranslatorCopyright" type="Bool"> + <label> + </label> + <default>true</default> + </entry> + <entry name="UseOldEncoding" type="Bool"> + <label>If the saving should preserve the encoding of the file, if already defined + </label> + <default>true</default> + </entry> + </group> + <group name="Misc"> + <entry name="AccelMarker" type="String"> + <label>Marker for accelerators</label> + <default>&</default> + </entry> + <entry name="BZipCompression" type="Bool"> + <label>If the files should be compressed for mailing</label> + <default>true</default> + </entry> + <entry name="CompressSingleFile" type="Bool"> + <label>If even single file should be compressed for mailing</label> + <default>true</default> + </entry> + <entry name="ContextInfo" type="String"> + <label>Regular expression for identifying a context information in original text</label> + <default>^_:((?!\\n\n).)+\\n\n</default> + </entry> + <entry name="MailArchiveNames" type="StringList"> + <label>List of recent mailed archives</label> + <default></default> + </entry> + <entry name="SingularPlural" type="String"> + <label>Regular expression for identifying a KDE plural form</label> + <default>_n:\s</default> + </entry> + <entry name="DiffBaseDir" type="String"> + <label> + </label> + <default></default> + </entry> + <entry name="UseDBForDiff" type="Enum"> + <label> + </label> + <choices> + <choice name="File"/> + <choice name="Database"/> + <choice name="Msgstr"/> + </choices> + <default>Database</default> + </entry> + </group> + <group name="Project"> + <entry name="Name" type="String"> + <label>Name of the project</label> + <default></default> + </entry> + <entry name="Version" type="String"> + <label>Version of the configuration file</label> + <default></default> + </entry> + </group> + <group name="Spellcheck"> + <entry name="IgnoreURL" type="String"> + <label> + </label> + <default code="true">Defaults::Spellcheck::ignoreURL()</default> + </entry> + <entry name="NoRootAffix" type="Bool"> + <label> + </label> + <default code="true">Defaults::Spellcheck::noRootAffix()</default> + </entry> + <entry name="OnFlySpellCheck" type="Bool"> + <label> + </label> + <default>true</default> + </entry> + <entry name="RememberIgnored" type="Bool"> + <label> + </label> + <default>true</default> + </entry> + <entry name="RunTogether" type="Bool"> + <label> + </label> + <default code="true">Defaults::Spellcheck::runTogether()</default> + </entry> + <entry name="SpellClient" type="UInt"> + <label> + </label> + <default code="true">Defaults::Spellcheck::spellClient()</default> + </entry> + <entry name="SpellDictionary" type="String"> + <label> + </label> + <default code="true">Defaults::Spellcheck::spellDictionary()</default> + </entry> + <entry name="SpellEncoding" type="UInt"> + <label> + </label> + <default code="true">Defaults::Spellcheck::spellEncoding()</default> + </entry> + </group> + <group name="Tags"> + <entry name="TagExpressions" type="String"> + <label> + </label> + <default code="yes">Defaults::Tags::tagExpressions()</default> + </entry> + </group> + <group name="SourceContext"> + <entry name="CodeRoot" type="String"> + <default></default> + </entry> + <entry name="Paths" type="StringList"> + <default>@PACKAGEDIR@/@PACKAGE@/@COMMENTPATH@,@CODEROOT@/@PACKAGEDIR@/@PACKAGE@/@COMMENTPATH@,@CODEROOT@/@PACKAGE@/@COMMENTPATH@,@POFILEDIR@/@COMMENTPATH@,@POFILEDIR@/../@COMMENTPATH@</default> + </entry> + </group> +</kcfg> diff --git a/kbabel/common/kbprojectsettings.kcfgc b/kbabel/common/kbprojectsettings.kcfgc new file mode 100644 index 00000000..bf7df895 --- /dev/null +++ b/kbabel/common/kbprojectsettings.kcfgc @@ -0,0 +1,7 @@ +File=kbprojectsettings.kcfg +NameSpace=KBabel +ClassName=ProjectSettingsBase +Singleton=false +Mutators=true +GlobalEnums=true + diff --git a/kbabel/common/libgettext/Makefile.am b/kbabel/common/libgettext/Makefile.am new file mode 100644 index 00000000..eca54fda --- /dev/null +++ b/kbabel/common/libgettext/Makefile.am @@ -0,0 +1,15 @@ +# this might be a naive way of seeing it, but +# automake does not support C++ flex files. +#LEX_OUTPUT_ROOT = lex.GettextBase +#AM_LFLAGS = -+ + +CLEANFILES = pofiles.cc + +noinst_LTLIBRARIES = libgettext.la + +libgettext_la_SOURCES = pofiles.cc + +pofiles.cc: $(srcdir)/pofiles.ll + $(LEX) -+ -opofiles.cc $(srcdir)/pofiles.ll + +noinst_HEADERS = tokens.h pofiles.h diff --git a/kbabel/common/libgettext/pofiles.h b/kbabel/common/libgettext/pofiles.h new file mode 100644 index 00000000..cf2e3ec8 --- /dev/null +++ b/kbabel/common/libgettext/pofiles.h @@ -0,0 +1,52 @@ +/* + This file is part of KBabel + + Copyright (c) 2005 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., 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. + +*/ + +#ifndef POFILES_H +#define POFILES_H + +#undef yyFlexLexer +#define yyFlexLexer GettextBaseFlexLexer +#include <FlexLexer.h> + +class GettextFlexLexer: public GettextBaseFlexLexer { +public: + int lastToken; + + GettextFlexLexer(std::istream*stream) : GettextBaseFlexLexer(stream) {} + + int yylex() + { + return lastToken = GettextBaseFlexLexer::yylex(); + } +}; + +#endif diff --git a/kbabel/common/libgettext/pofiles.ll b/kbabel/common/libgettext/pofiles.ll new file mode 100644 index 00000000..7466487a --- /dev/null +++ b/kbabel/common/libgettext/pofiles.ll @@ -0,0 +1,107 @@ + +/* + This file is a part of KBabel + + Copyright (c) 2005 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. + + 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. + +*/ + +%{ +#define YY_NO_UNPUT + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> + +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif + +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +#include "tokens.h" + + +%} + +%option prefix="GettextBase" + +%option noyywrap + +/* Any end of line */ +EOL [\r\n] +/* No end of line */ +NOEOL [^\r\n] + +%% + + + +[ ]*{EOL}+ ; /* This catches empty lines or a LF after a CR */ + +msgid { + return T_MSGID; + } +msgid_plural { + return T_MSGIDPLURAL; + } +msgstr\[[0-9]+\] { + return T_MSGSTR; + } +msgstr { + return T_MSGSTR; + } +msgctxt { + return T_MSGCTXT; + } +#~{NOEOL}* { + return T_OBSOLETE; + } + +#{NOEOL}* { + return T_COMMENT; + } + +\"({NOEOL}*(\\\")?)*\"{EOL} { + yytext[strlen(yytext)-2] = 0; + yytext++; + return T_STRING; + } + +[a-z]+ | +. ; + +<<EOF>> { + return T_EOF; + } + +%% diff --git a/kbabel/common/libgettext/tokens.h b/kbabel/common/libgettext/tokens.h new file mode 100644 index 00000000..dfbf693d --- /dev/null +++ b/kbabel/common/libgettext/tokens.h @@ -0,0 +1,45 @@ +/* + This file is a part of KBabel + + Copyright (c) 2005 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., 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. + +*/ + +#ifndef POTOKENS_H +#define POTOKENS_H + +#define T_EOF 256 +#define T_COMMENT 257 +#define T_STRING 258 +#define T_MSGID 259 +#define T_MSGSTR 260 +#define T_OBSOLETE 261 +#define T_MSGIDPLURAL 262 +#define T_MSGCTXT 263 + +#endif diff --git a/kbabel/common/msgfmt.cpp b/kbabel/common/msgfmt.cpp new file mode 100644 index 00000000..ef8ca895 --- /dev/null +++ b/kbabel/common/msgfmt.cpp @@ -0,0 +1,147 @@ +/* **************************************************************************** + This file is part of KBabel + + Copyright (C) 1999-2000 by Matthias Kiefer + <matthias.kiefer@gmx.de> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 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 "msgfmt.h" + +#include <kapplication.h> +#include <kprocess.h> + +#include <qfileinfo.h> +#include <qdir.h> +#include <qfile.h> +#include <qregexp.h> +#include <qtextstream.h> +#include <qstring.h> + +#include <stdlib.h> + +using namespace KBabel; + +Msgfmt::Msgfmt(QObject* parent,const char* name) + : QObject(parent , name) +{ +} + +Msgfmt::Status Msgfmt::checkSyntax(QString file,QString& output, bool gnu) +{ + Status stat=Ok; + // this method does not return the right retrun values at the moment :-( + + KProcess proc; + + connect(&proc,SIGNAL(receivedStdout(KProcess*, char*, int)), + this,SLOT(addToOutput(KProcess*,char *, int ))); + connect(&proc,SIGNAL(receivedStderr(KProcess *, char *, int)), + this,SLOT(addToOutput(KProcess*,char *, int))); + + // remove last output + _output=""; + + + proc << "msgfmt" << "--statistics" << "-o" << "/dev/null" << file; + + if( gnu ) + { + proc << "-vc"; + } + + if(!proc.start(KProcess::Block,KProcess::Stderr)) + { + stat=NoExecutable; + } + else if(proc.normalExit()) + { + if( proc.exitStatus() || _output.contains(QRegExp("^.+:\\d+:")) ) // little workaround :-( + stat=SyntaxError; + } + else + stat=Error; + + output=_output; + + + return stat; +} + +Msgfmt::Status Msgfmt::checkSyntaxInDir(QString dir,QString regexp,QString& output) +{ + Status stat=Ok; + + // this method does not return the right return values at the moment :-( + KProcess proc; + proc.setUseShell(true); + + connect(&proc,SIGNAL(receivedStdout(KProcess*, char*, int)), + this,SLOT(addToOutput(KProcess*,char *, int ))); + connect(&proc,SIGNAL(receivedStderr(KProcess *, char *, int)), + this,SLOT(addToOutput(KProcess*,char *, int))); + + // remove last output + _output=""; + + proc << "IFS='\n'; msgfmt --statistics -o /dev/null " + "$(find" << KProcess::quote(dir) << "-name" << KProcess::quote(regexp) << ")"; + + if(!proc.start(KProcess::Block,KProcess::Stderr)) + { + stat=NoExecutable; + } + else if(proc.normalExit()) + { + if( proc.exitStatus() || _output.contains(QRegExp("^.+:\\d+:")) ) // little workaround :-( + stat=SyntaxError; + } + else + stat=Error; + + output=_output; + + + return stat; +} + + + +void Msgfmt::addToOutput(KProcess*,char *buffer, int buflen) +{ + QString newString = QString::fromLocal8Bit(buffer, buflen); + + _output+=newString; +} + + +QString Msgfmt::tempSaveName() +{ + return kapp->tempSaveName("/tmp/kbabel_msgfmt.po"); +} + +#include "msgfmt.moc" diff --git a/kbabel/common/msgfmt.h b/kbabel/common/msgfmt.h new file mode 100644 index 00000000..bf983b2f --- /dev/null +++ b/kbabel/common/msgfmt.h @@ -0,0 +1,65 @@ +/* **************************************************************************** + This file is part of KBabel + + Copyright (C) 1999-2000 by Matthias Kiefer + <matthias.kiefer@gmx.de> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 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. + +**************************************************************************** */ +#ifndef MSGFMT_H +#define MSGFMT_H + +#include <qobject.h> +#include <kdemacros.h> + +class KProcess; +class QString; + +namespace KBabel +{ + +class KDE_EXPORT Msgfmt : private QObject +{ + Q_OBJECT +public: + enum Status{NoExecutable,Ok,SyntaxError,HeaderError,Error,Unsupported}; + Msgfmt(QObject* parent=0,const char* name=0); + + Status checkSyntax(QString file,QString& output, bool gnu = false); + Status checkSyntaxInDir(QString dir,QString regexp,QString& output); + +private slots: + void addToOutput(KProcess*,char *buffer, int buflen); + +private: + static QString tempSaveName(); + QString _output; +}; + +} + +#endif // MSGFMT_H diff --git a/kbabel/common/pluralforms.h b/kbabel/common/pluralforms.h new file mode 100644 index 00000000..f65dba01 --- /dev/null +++ b/kbabel/common/pluralforms.h @@ -0,0 +1,38 @@ +/* **************************************************************************** + This file is part of KBabel + + Copyright (C) 2002-2005 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., 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. + +**************************************************************************** */ +#ifndef PLURALFORMS_H +#define PLURALFORMS_H + +enum PluralFormType{NoPluralForm, Gettext, KDESpecific}; + +#endif diff --git a/kbabel/common/poinfo.cpp b/kbabel/common/poinfo.cpp new file mode 100644 index 00000000..b7beba7d --- /dev/null +++ b/kbabel/common/poinfo.cpp @@ -0,0 +1,781 @@ +/* + This file is part of KBabel + Copyright (C) 2002 Stefan Asserhäll <stefan.asserhall@telia.com> + 2003-2005 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., 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 "poinfo.h" + +#include "catalogitem.h" +#include "findoptions.h" +#include "msgfmt.h" +#include "resources.h" + +#include <kapplication.h> +#include <kio/netaccess.h> +#include <kstandarddirs.h> +#include <ksavefile.h> + +#include <qdatastream.h> +#include <qdatetime.h> +#include <qdict.h> +#include <qfile.h> +#include <qfileinfo.h> +#include <qregexp.h> +#include <qtextcodec.h> + +#include "libgettext/pofiles.h" +#include "libgettext/tokens.h" + +#include <fstream> + +using namespace KBabel; + +// A PO-file cache item +struct poInfoCacheItem +{ + PoInfo info; + QDateTime lastModified; +}; + +inline QDataStream& operator << ( QDataStream& stream, poInfoCacheItem* item ) +{ + // Note: if you change anything here, do not forget to increase the #define POINFOCACHE_VERSION + stream << item->info.total; + stream << item->info.fuzzy; + stream << item->info.untranslated; + stream << item->info.project; + stream << item->info.creation; + stream << item->info.revision; + stream << item->info.lastTranslator; + stream << item->info.languageTeam; + stream << item->info.mimeVersion; + stream << item->info.contentType; + stream << item->info.encoding; + stream << item->info.others; + stream << item->info.headerComment; + stream << item->lastModified; + return stream; +} + +inline QDataStream& operator >> ( QDataStream& stream, poInfoCacheItem* item ) +{ + stream >> item->info.total; + stream >> item->info.fuzzy; + stream >> item->info.untranslated; + stream >> item->info.project; + stream >> item->info.creation; + stream >> item->info.revision; + stream >> item->info.lastTranslator; + stream >> item->info.languageTeam; + stream >> item->info.mimeVersion; + stream >> item->info.contentType; + stream >> item->info.encoding; + stream >> item->info.others; + stream >> item->info.headerComment; + stream >> item->lastModified; + return stream; +} + +// Cache of PO-file items +static QDict<poInfoCacheItem> _poInfoCache; + +// File name of cache +static QString _poInfoCacheName; + +// flag to stop current reading +bool PoInfo::stopStaticRead; + +bool PoInfo::_gettextPluralForm; + +// Note: We only read the cache file if the data seems usable. If not, we will re-generate the data. +void PoInfo::cacheRead() +{ + QFile cacheFile( _poInfoCacheName ); + if( cacheFile.open( IO_ReadOnly ) ) { + QDataStream s( &cacheFile ); + + // Check the file cache version. + // If it is not the current version, we do not read the cache file + Q_UINT32 version; + s >> version; + if( version != POINFOCACHE_VERSION ) { + // Wrong POINFOCACHE_VERSION, so abort + kdDebug(KBABEL) << "Wrong cache file version: " << version << endl; + return; + } + + /* + * Check the version of the QDataStream with which the cache file was written + * + * If the cache file was written by an incompatible future version of Qt, + * the cache file will not be read. + * + * On the other side, a cache file written by a previous version of Qt can be read, + * by setting the version of the QDataStream used. + */ + Q_INT32 qdatastreamVersion; + s >> qdatastreamVersion; + if( qdatastreamVersion > 0 && qdatastreamVersion <= s.version() ) { + s.setVersion( qdatastreamVersion ); + } + else { + // QDataStream version seems stupid, so abort + kdDebug(KBABEL) << "Wrong QDataStream version: " << qdatastreamVersion << endl; + return; + } + + QString url; + while( !s.atEnd() ) { + poInfoCacheItem* item = new poInfoCacheItem; + s >> url; + s >> item; + _poInfoCache.insert( url, item ); + } + cacheFile.close(); + } +} + +void PoInfo::cacheWrite() +{ + // We use KSaveFile as otherwise we have no management about the cache file's integrity + // (especially if two instances would write into the same cache file) + KSaveFile cacheFile( _poInfoCacheName ); + + QDataStream* stream = cacheFile.dataStream(); + + if( stream ) { + + // Write the cache file version + // We choose to fix a format (Q_UINT32) for compatibility (Qt version, platforms, architectures) + const Q_UINT32 version = POINFOCACHE_VERSION; + *stream << version; + + // Write the version of the QDataStream + // Here too we choose a fixed format (Q_INT32) for compatibility + const Q_INT32 qdatastreamVersion = stream->version(); + *stream << qdatastreamVersion; + + QDictIterator<poInfoCacheItem> it( _poInfoCache ); // iterator for dict + for ( ; it.current(); ++it ) { + if( QFile::exists( it.currentKey() ) ) { + *stream << it.currentKey(); + *stream << it.current(); + } + } + if ( !cacheFile.close() ) { + kdWarning(KBABEL) << "Could not write cache file: " << _poInfoCacheName << endl; + } + } + else { + kdWarning(KBABEL) << "Could not create QDataStream for cache file: " << _poInfoCacheName << endl; + cacheFile.abort(); + } +} + +bool PoInfo::cacheFind(const QString url, PoInfo& info) +{ + // Read cache if it has not been read, and set up post routine to write it + static bool _cacheIsRead = false; + if( !_cacheIsRead ) { + _cacheIsRead = true; + _poInfoCacheName = locateLocal("cache", "kbabel/poinfocache"); + cacheRead(); + } + + poInfoCacheItem *item = _poInfoCache.find( url ); + if( item ) { + QFileInfo fi( url ); + + if( fi.lastModified() == item->lastModified ) { + info = item->info; + return true; + } + } + return false; +} + +void PoInfo::cacheSave(const QString url, PoInfo& info) +{ + poInfoCacheItem *item = new poInfoCacheItem; + QFileInfo fi( url ); + + item->info = info; + item->lastModified = fi.lastModified(); + _poInfoCache.insert( url, item ); +} + +QTextCodec* PoInfo::codecForFile(QString gettextHeader) +{ + QRegExp regexp("Content-Type:\\s*\\w+/[-\\w]+;?\\s*charset\\s*=\\s*(\\S+)\\s*\\\\n"); + if( regexp.search(gettextHeader) == -1 ) + { + kdDebug(KBABEL) << "no charset entry found" << endl; + return 0; + } + + const QString charset = regexp.cap(1); + kdDebug(KBABEL) << "charset: " << charset << endl; + + 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(KBABEL) << "charset found, but no codec available, using UTF8 instead" << endl; + codec=QTextCodec::codecForName("utf8"); + } + } + else + { + // No charset? So it is probably ASCII, therefore UTF-8 + kdWarning(KBABEL) << "No charset defined! Assuming UTF-8!" << endl; + codec=QTextCodec::codecForName("utf8"); + } + + return codec; +} + +PoInfo PoInfo::headerInfo(const CatalogItem& headerItem) +{ + // A header of a Gettext .po/.pot file is made of entries of the kind: + // key:value\n + // Note that the "line" defined by the \n can be different than the line of the file. + + // We join all lines of the header and then split the result again at the \n sequence + const QStringList header=QStringList::split("\\n",headerItem.msgstrAsList().join(QString())); + + PoInfo info; + + // extract information from the header + QStringList::const_iterator it; + + // The header of a Gettext .po file is consisted of lines of key and value + for(it=header.begin();it!=header.end();++it) + { + bool knownKey=false; + // We search for the : character, which is the separator between key and value + const int res=(*it).find(':'); + if (res>=0) + { + knownKey=true; // We know most keys, if not it will be changed to false in the "else" case + const QString key=(*it).left(res).simplifyWhiteSpace(); + QString value=(*it).mid(res+1); + // "Chop" the \n at the end + if (value.endsWith("\\n")) + value.remove(value.length()-2,2); // ### Qt4: use value.chop(2) + value=value.simplifyWhiteSpace(); + kdDebug(KBABEL) << "Header key: " << key << " value: " << value << endl; + if (key=="Project-Id-Version") + info.project=value; + else if (key=="POT-Creation-Date") + info.creation=value; + else if (key=="PO-Revision-Date") + info.revision=value; + else if (key=="Last-Translator") + info.lastTranslator=value; + else if (key=="Language-Team") + info.languageTeam=value; + else if (key=="MIME-Version") + info.mimeVersion=value; + else if (key=="Content-Type") + info.contentType=value; + else if (key=="Content-Transfer-Encoding") + info.encoding=value; + else + { + kdDebug(KBABEL)<<"Unknown key: "<<key<<endl; + knownKey=false; + } + } + if (!knownKey) + { + QString line=(*it); + + if(line.right(2)=="\\n") + line.remove(line.length()-2,2); // ### Qt4: use value.chop(2) + + if(!info.others.isEmpty()) + info.others+='\n'; + + info.others+=line.simplifyWhiteSpace(); + } + } + + info.headerComment=headerItem.comment(); + + return info; +} + + +ConversionStatus PoInfo::info(const QString& url, PoInfo& info, QStringList &wordList, bool updateWordList, bool interactive) +{ + return PoInfo::info( url, info, wordList, updateWordList, interactive, true); +} + +ConversionStatus PoInfo::info(const QString& url, PoInfo& info, QStringList &wordList, bool updateWordList, bool interactive, bool msgfmt) +{ + stopStaticRead = false; + + if( !updateWordList && PoInfo::cacheFind( url, info ) ) + return OK; + + QString target; + if(KIO::NetAccess::download(KURL( url ), target, 0)) + { + QFile file(target); + + if ( msgfmt ) + { + // First check file with msgfmt to be sure, it is syntactically correct + Msgfmt msgfmt; + QString output; + Msgfmt::Status stat = msgfmt.checkSyntax( target , output ); + if(stat == Msgfmt::SyntaxError) + { + KIO::NetAccess::removeTempFile(target); + return PARSE_ERROR; + } + } + + + std::ifstream* stream = new std::ifstream( file.name().local8Bit()); + if( stream->is_open() ) + { + CatalogItem temp; + + info.total=0; + info.fuzzy=0; + info.untranslated=0; + + GettextFlexLexer* lexer = new GettextFlexLexer( stream ); + + lexer->yylex(); + + // now parse the rest of the file + ConversionStatus success=OK; + + while( lexer->lastToken != T_EOF && success==OK) + { + if( interactive ) kapp->processEvents(10); + + if( stopStaticRead ) + { + KIO::NetAccess::removeTempFile(target); + delete lexer; + delete stream; + return OK; + } + + success=fastRead(temp,lexer,false); + + if(success==OK || success==RECOVERED_PARSE_ERROR) + { + success=OK; + + if( temp.comment().contains("\n#~") ) continue; // skip obsolete + + if( temp.msgid().first().isEmpty()) //header + { + if( temp.isFuzzy() ) temp.removeFuzzy(); + + //find out the codec + QTextCodec* codec = codecForFile( temp.msgstr().first() ); + if( !codec ) return PARSE_ERROR; + + // convert from UTF-8 using codec + temp.setComment( codec->toUnicode(temp.comment().utf8()) ); + temp.setMsgstr( codec->toUnicode(temp.msgstr().first().utf8()) ); + + PoInfo infoCounts = info; + info=PoInfo::headerInfo(temp); + info.total = infoCounts.total; + info.fuzzy = infoCounts.fuzzy; + info.untranslated = infoCounts.untranslated; + continue; // do not update counters and word list for header + } + + info.total++; + + if(temp.isFuzzy()) + info.fuzzy++; + else if(temp.isUntranslated()) + info.untranslated++; + + if( updateWordList ) + { + // FIXME: should care about plural forms in msgid + QString st = temp.msgid().first().simplifyWhiteSpace().lower(); + QStringList sl = QStringList::split( ' ', st ); + while(!sl.isEmpty()) + { + QString w = sl.first(); + sl.pop_front(); + if( !wordList.contains(w) ) wordList.append( w ); + } + st = temp.msgstr().join(" " ).simplifyWhiteSpace().lower(); + sl = QStringList::split( ' ', st ); + while(!sl.isEmpty()) + { + QString w = sl.first(); + sl.pop_front(); + if( !wordList.contains(w) ) wordList.append( w ); + } + st = temp.comment().simplifyWhiteSpace().lower(); + sl = QStringList::split( ' ', st ); + while(!sl.isEmpty()) + { + QString w = sl.first(); + sl.pop_front(); + if( !wordList.contains(w) ) wordList.append( w ); + } + } + } + } + + delete lexer; + delete stream; + + if(success==PARSE_ERROR) + { + KIO::NetAccess::removeTempFile(target); + return PARSE_ERROR; + } + } + else + { + delete stream; + KIO::NetAccess::removeTempFile(target); + return NO_PERMISSIONS; + } + + KIO::NetAccess::removeTempFile(target); + if( target == url ) + PoInfo::cacheSave( url, info ); + return OK; + } + else + { + return OS_ERROR; + } + + return OK; +} + +bool PoInfo::findInFile( const QString& url, FindOptions options ) +{ + enum {Begin, Comment, Msgid, Msgstr, Msgctxt} part = Begin; + + stopStaticRead = false; + QString target; + if(KIO::NetAccess::download(KURL( url ), target, 0)) + { + std::ifstream* stream = new std::ifstream( target.local8Bit()); + if(stream->is_open()) + { + KIO::NetAccess::removeTempFile(target); + + GettextFlexLexer* lexer = new GettextFlexLexer( stream ); + + lexer->yylex(); + + // prepare the search + + QString searchStr = options.findStr; + QRegExp regexp( searchStr ); + + if( options.isRegExp ) + regexp.setCaseSensitive( options.caseSensitive ); + + // first read header + CatalogItem temp; + + ConversionStatus status = fastRead( temp, lexer, true ); + if( status != OK || !temp.msgid().first().isEmpty() ) + { + delete lexer; + delete stream; + return false; // header is not at the beginning, broken file + } + + QTextCodec* codec = codecForFile( temp.msgstr().first() ); + if( !codec ) + { + return false; + } + + // now parse the rest of the file + QString text; + int pos,len; + + while(lexer->lastToken != T_EOF) + { + switch( lexer->lastToken ) { + case T_COMMENT: { + part = Comment; + if( !options.inComment ) break; + text = codec->toUnicode(lexer->YYText()); + if( options.isRegExp ) + pos=regexp.search(text, 0 ); + else + pos=text.find(searchStr,0,options.caseSensitive); + if( pos >= 0) + { + if( options.wholeWords) { + len = searchStr.length(); + QString pre = text.mid(pos-1,1); + QString post = text.mid(pos+len,1); + if( !pre.contains( QRegExp("[a-zA-Z0-9]")) && + !post.contains( QRegExp("[a-zA-Z0-9]") ) + ) { + delete lexer; + delete stream; + return true; + } + } + else { + delete lexer; + delete stream; + return true; + }; + } + break; + } + case T_STRING: { + if( part == Msgid && !options.inMsgid ) break; + else if( part == Msgstr && !options.inMsgstr ) break; + // HACK: We ignore any string following a msgctxt, as it does not change a statistic + else if( part == Msgctxt ) break; + + text = codec->toUnicode(lexer->YYText()); + + if( options.ignoreContextInfo ) + { + pos = options.contextInfo.search(text); + len = options.contextInfo.matchedLength(); + if( pos >= 0 ) + text.remove( pos, len ); + } + + if( options.ignoreAccelMarker ) + { + pos = text.find( options.accelMarker ); + if( pos >= 0 ) + text.remove( pos, 1 ); + } + + if( options.isRegExp ) + pos=regexp.search(text, 0 ); + else + pos=text.find(searchStr,0,options.caseSensitive); + + if( pos >= 0) + { + if( options.wholeWords) { + len = searchStr.length(); + QString pre = text.mid(pos-1,1); + QString post = text.mid(pos+len,1); + if( !pre.contains( QRegExp("[a-zA-Z0-9]")) && + !post.contains( QRegExp("[a-zA-Z0-9]") ) + ) { + delete lexer; + delete stream; + return true; + } + } + else { + delete lexer; + delete stream; + return true; + }; + } + break; + } + case T_MSGSTR: { + part = Msgstr; + break; + } + case T_MSGID: + case T_MSGIDPLURAL: { + kapp->processEvents(10); + + // if stopped, return not found + if( stopStaticRead ) + { + delete lexer; + delete stream; + return false; + } + part = Msgid; + break; + } + case T_MSGCTXT: { + part = Msgctxt; + break; + } + } + lexer->yylex(); + } + delete lexer; + delete stream; + } + } + return false; +} + +// this does not like any incorrect files +ConversionStatus PoInfo::fastRead( CatalogItem& item, GettextFlexLexer *lexer, bool storeText) +{ + item.clear(); + _gettextPluralForm = false; + + // comment + if( lexer->lastToken == T_COMMENT ) + { + QString _comment = QString::fromUtf8(lexer->YYText()); + while( lexer->yylex() == T_COMMENT ) + _comment += "\n"+QString::fromUtf8(lexer->YYText()); + item.setComment( _comment ); +// kdDebug(KBABEL) << "Comment: " << _comment << endl; + } + + //obsolete + if( lexer->lastToken == T_OBSOLETE ) { + lexer->yylex(); + item.setComment("#~\n#~"); + return OK; + } + + // msgctxt + if( lexer->lastToken == T_MSGCTXT ) { + // HACK: we simply ignore the context, as it does not change a statistic + do { + lexer->yylex(); + } while ( lexer->lastToken == T_STRING ); + } + + // msgid + if( lexer->lastToken != T_MSGID ) return PARSE_ERROR; + + if( lexer->yylex() != T_STRING ) return PARSE_ERROR; + QStringList msgids = item.msgid(); + QStringList::Iterator it = msgids.begin(); + *it = QString::fromUtf8(lexer->YYText()); + if( storeText ) + while( lexer->yylex() == T_STRING ) + (*it) += ("\n"+ QString::fromUtf8(lexer->YYText()) ); + else { + if( lexer->yylex() == T_STRING ) // this is not header + { + *it = "SKIPPED"; + while( lexer->yylex() == T_STRING ); + } + } + item.setMsgid( msgids ); + +// kdDebug(KBABEL) << "Msgid: " << *it << endl; + + if( lexer->lastToken == T_MSGIDPLURAL ) + { + _gettextPluralForm = true; + if( lexer->yylex() != T_STRING ) return PARSE_ERROR; + QStringList msgids = item.msgid(); + it = msgids.fromLast(); + *it = QString::fromUtf8(lexer->YYText()); + if( storeText ) + while( lexer->yylex() == T_STRING ) + (*it)+="\n"+ QString::fromUtf8(lexer->YYText()); + else while( lexer->yylex() == T_STRING ); + item.setMsgid( msgids ); +// kdDebug(KBABEL) << "Msgid_plural: " << *it << endl; + } + + // msgstr + if( lexer->lastToken != T_MSGSTR ) return PARSE_ERROR; + + if( !_gettextPluralForm ) + { + if( lexer->yylex() != T_STRING ) return PARSE_ERROR; + + QStringList msgstrs = item.msgstr(); + it = msgstrs.begin(); + *it = QString::fromUtf8(lexer->YYText()); + if( storeText || item.msgid().first().isEmpty() ) // if we should store the text or it is a header + while( lexer->yylex() == T_STRING ) + (*it)+= ("\n"+ QString::fromUtf8(lexer->YYText())); + else + if( lexer->yylex() == T_STRING ) // check next token, whether it is really translated + { + *it = "SKIPPED"; + while( lexer->yylex() == T_STRING ); + } + item.setMsgstr( msgstrs ); +// kdDebug(KBABEL) << "Msgstr: " << *it << endl; + } + else + { + QStringList msgstrs = item.msgstr(); + QString s = QString::fromUtf8(lexer->YYText()); + while( lexer->lastToken == T_MSGSTR && s.contains( QRegExp("^msgstr\\[[0-9]+\\]" ) ) ) + { + if( lexer->yylex() != T_STRING ) return PARSE_ERROR; + it = msgstrs.fromLast(); + *it = QString::fromUtf8(lexer->YYText()); + + if( storeText ) + do { + (*it)+="\n"+QString::fromUtf8(lexer->YYText()); + } while( lexer->yylex() == T_STRING ); + else while( lexer->yylex() == T_STRING ); +// kdDebug(KBABEL) << "Msgstr: " << *it << endl; + s = QString::fromUtf8(lexer->YYText()); + } + item.setMsgstr( msgstrs ); + } + + return OK; +} + +// kate: space-indent on; indent-width 4; replace-tabs on; diff --git a/kbabel/common/poinfo.h b/kbabel/common/poinfo.h new file mode 100644 index 00000000..21b8d72d --- /dev/null +++ b/kbabel/common/poinfo.h @@ -0,0 +1,157 @@ +/* + This file is part of KBabel + Copyright (C) 2002 Stefan Asserhäll <stefan.asserhall@telia.com> + 2003-2005 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., 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. + +*/ + +#ifndef POINFO_H +#define POINFO_H + + +#include "catalogfileplugin.h" + +#include <kdebug.h> + +#include <qstring.h> + +/** + * @brief File cache version number. + * + * @note If the existing file is outdated, it will not be read, + * instead we will overwrite it with a new file. + */ +#define POINFOCACHE_VERSION 2 + +class GettextFlexLexer; + +namespace KBabel { + +class FindOptions; + +/** + * PO-file information class, with transparent caching of information. + */ +class KDE_EXPORT PoInfo +{ +public: + int total; + int fuzzy; + int untranslated; + + QString project; + QString creation; + QString revision; + QString lastTranslator; + QString languageTeam; + QString mimeVersion; + QString contentType; + QString encoding; + QString others; + + QString headerComment; + + /** + * Find PO-file information in the cache. + * + * @param url The URL of the PO-file. + * @param info Returned cached information. + * @return true if found, false otherwise. + */ + static bool cacheFind(const QString url, PoInfo& info); + + /** + * Save PO-file information in the cache. + * + * @param url The URL of the PO-file. + * @param info Information to save. + */ + static void cacheSave(const QString url, PoInfo& info); + + /** + * @brief Get information about the PO file + * + * @param url The path (not an URL) of the file + * @param info The information retrieved from the file + * @param wordList ??? + * @param updateWordList ??? + * @param interactive Can the function interact with the user? + * @param msgfmt Should each file be checked with Gettext's msgfmt before + * being parsed? + * @since KBabel 1.11 (KDE 3.5) + */ + static ConversionStatus info(const QString& url,PoInfo& info, QStringList &wordList, bool updateWordList, bool interactive, bool msgfmt); + + /** + * @brief Get information about the PO file + * + * @param url The path (not an URL) of the file + * @param info The information retrieved from the file + * @param wordList ??? + * @param updateWordList ??? + * @param interactive Can the function interact with the user? + * @deprecated + * @note This function is missing in KBabel 1.11 (KDE 3.5). + * The function was re-introduced for binary-compatibility in + * KBabel 1.11.1 (KDE 3.5.1) + * @note This function always call Gettext's mgfmt before parsing each file + */ + static ConversionStatus info(const QString& url,PoInfo& info, QStringList &wordList, bool updateWordList, bool interactive = true); + + static PoInfo headerInfo(const CatalogItem&); + static bool findInFile(const QString& url, FindOptions options ); + + static bool stopStaticRead; + + /** + * reads header information from the file and searches for charset + * information. + * @param gettextHeader text containing gettext headers + * + * @return Codec for found charset or 0, if no information has been found + */ + static QTextCodec* codecForFile(QString gettextHeader); + + /** + * @brief Write the entire cache. + */ + static void cacheWrite(); +private: + /** + * Read the entire cache. + */ + static void cacheRead(); + + static ConversionStatus fastRead( CatalogItem& item, GettextFlexLexer* lexer, bool storeText ); + + static bool _gettextPluralForm; +}; + +} + +#endif // POINFO_H diff --git a/kbabel/common/projectsettings.cpp b/kbabel/common/projectsettings.cpp new file mode 100644 index 00000000..3687c9d4 --- /dev/null +++ b/kbabel/common/projectsettings.cpp @@ -0,0 +1,126 @@ +/* **************************************************************************** + 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., 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 "projectsettings.h" +#include "resources.h" + +#include <qstring.h> +#include <kglobal.h> +#include <klocale.h> +#include <kstandarddirs.h> + +#include <kspell.h> + +QString KBabel::Defaults::Spellcheck::ignoreURL() +{ + QString _ignoreURL; + + KStandardDirs * dirs = KGlobal::dirs(); + if(dirs) + { + _ignoreURL = dirs->saveLocation("appdata"); + if(_ignoreURL.right(1)!="/") + _ignoreURL+="/"; + _ignoreURL += "spellignores"; + } + return _ignoreURL; +} + +KSpellConfig* KBabel::Defaults::Spellcheck::defaultsettings = NULL; + +bool KBabel::Defaults::Spellcheck::noRootAffix() +{ + if( ! defaultsettings ) + { + defaultsettings = new KSpellConfig(0, "spellconfig"); + } + + return defaultsettings->noRootAffix(); +} + +bool KBabel::Defaults::Spellcheck::runTogether() +{ + if( ! defaultsettings ) + { + defaultsettings = new KSpellConfig(0, "spellconfig"); + } + + return defaultsettings->runTogether(); +} + +int KBabel::Defaults::Spellcheck::spellClient() +{ + if( ! defaultsettings ) + { + defaultsettings = new KSpellConfig(0, "spellconfig"); + } + + return defaultsettings->client(); +} + +QString KBabel::Defaults::Spellcheck::spellDictionary() +{ + if( ! defaultsettings ) + { + defaultsettings = new KSpellConfig(0, "spellconfig"); + } + + return defaultsettings->dictionary(); +} + +int KBabel::Defaults::Spellcheck::spellEncoding() +{ + if( ! defaultsettings ) + { + defaultsettings = new KSpellConfig(0, "spellconfig"); + } + + return defaultsettings->encoding(); +} + +QString KBabel::Defaults::CatalogManager::ignoreURL() +{ + QString _ignoreURL; + + KStandardDirs * dirs = KGlobal::dirs(); + if(dirs) + { + _ignoreURL = dirs->saveLocation("appdata"); + if(_ignoreURL.right(1)!="/") + _ignoreURL+="/"; + _ignoreURL += "validationignores"; + } + return _ignoreURL; +} + diff --git a/kbabel/common/projectsettings.h b/kbabel/common/projectsettings.h new file mode 100644 index 00000000..186b21e5 --- /dev/null +++ b/kbabel/common/projectsettings.h @@ -0,0 +1,142 @@ +/* **************************************************************************** + This file is part of KBabel + + Copyright (C) 1999-2000 by Matthias Kiefer + <matthias.kiefer@gmx.de> + 2001-2005 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., 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. + +**************************************************************************** */ +#ifndef PROJECTSETTINGS_H +#define PROJECTSETTINGS_H + +#include <qstring.h> +#include <qstringlist.h> +#include <qfont.h> +#include <qcolor.h> +#include <kurl.h> + +#include "catalogsettings.h" + +class KSpellConfig; + +namespace KBabel { + +struct SpellcheckSettings +{ + bool valid; + bool noRootAffix; + bool runTogether; + int spellEncoding; + int spellClient; + QString spellDict; + + bool rememberIgnored; + QString ignoreURL; + + bool onFlySpellcheck; + + SpellcheckSettings() { valid=false; } +}; + +struct CatManSettings +{ + QString poBaseDir; + QString potBaseDir; + + bool openWindow; + + QStringList dirCommands; + QStringList dirCommandNames; + QStringList fileCommands; + QStringList fileCommandNames; + + QString ignoreURL; + + bool killCmdOnExit; + bool indexWords; + /// Should be msgfmt be run before processing a file? + bool msgfmt; + + bool flagColumn; + bool fuzzyColumn; + bool untranslatedColumn; + bool totalColumn; + bool cvsColumn; + bool revisionColumn; + bool translatorColumn; + +}; + +struct SourceContextSettings +{ + /** + * A path, which can be used as @CODEROOT@ variable in @ref sourcePaths . + * Defaults to empty string. + */ + QString codeRoot; + + /** + * List of paths, where the source file should be lookup. Can use @CODEROOT@ (replaced by @ref codeRoot), + * @PACKAGE@ (replaced by package name), @PACKAGEDIR@ (replaced by the + * longest known path of the package) and @COMMENTPATH@ (path extracted from comment specs. + */ + QStringList sourcePaths; + + void SourceContextSettins() { codeRoot = QString::null; sourcePaths.clear(); } +}; + +/** +* This namespace provides static methods and variables to get the default +* values of configuration values +*/ +namespace Defaults +{ + class KDE_EXPORT Spellcheck + { + public: + static QString ignoreURL(); + static bool noRootAffix(); + static bool runTogether(); + static int spellClient(); + static QString spellDictionary(); + static int spellEncoding(); + private: + static KSpellConfig* defaultsettings; + }; + + class KDE_EXPORT CatalogManager + { + public: + static QString ignoreURL(); + }; +} + +} + +#endif // PROJECTSETTINGS_H diff --git a/kbabel/common/regexpextractor.cpp b/kbabel/common/regexpextractor.cpp new file mode 100644 index 00000000..b69398c6 --- /dev/null +++ b/kbabel/common/regexpextractor.cpp @@ -0,0 +1,271 @@ +/* **************************************************************************** + This file is part of KBabel + + Copyright (C) 2001 by Matthias Kiefer <matthias.kiefer@gmx.de> + 2002 by Stanislav Visnovsky <visnovsky@nenya.ms.mff.cuni.cz> + + based on code of Andrea Rizzi <rizzi@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the 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 "regexpextractor.h" + +#include <kdebug.h> +#include <qregexp.h> + +using namespace KBabel; + +RegExpExtractor::RegExpExtractor(const QStringList& regexps) : + _regExpList( regexps ) +{ + _string=QString::null; + _matches.setAutoDelete(true); +} + +void RegExpExtractor::setString(QString string) +{ + _string=string; + processString(); +} + +uint RegExpExtractor::countMatches() +{ + return _matches.count(); +} + +QString RegExpExtractor::firstMatch() +{ + MatchedEntryInfo *ti = _matches.first(); + + if(ti) + return ti->extracted; + + return QString::null; +} + +QString RegExpExtractor::nextMatch() +{ + MatchedEntryInfo *ti=_matches.next(); + if(!ti) + ti=_matches.first(); + + if(ti) + return ti->extracted; + + return QString::null; +} + +QString RegExpExtractor::match(uint tagnumber) +{ + MatchedEntryInfo *ti=_matches.at(tagnumber); + if(ti) + return ti->extracted; + + return QString::null; +} + +int RegExpExtractor::matchIndex(uint tagnumber) +{ + MatchedEntryInfo *ti=_matches.at(tagnumber); + if(ti) + return ti->index; + + return -1; +} + +QString RegExpExtractor::prevMatch() +{ + MatchedEntryInfo *ti=_matches.prev(); + if(ti) + return ti->extracted; + + return QString::null; +} + +QString RegExpExtractor::lastMatch() +{ + MatchedEntryInfo *ti=_matches.last(); + if(ti) + return ti->extracted; + + return QString::null; +} + +QStringList RegExpExtractor::matches() +{ + QStringList list; + MatchedEntryInfo *ti; + for(ti=_matches.first(); ti!=0; ti = _matches.next()) + { + list.append(ti->extracted); + } + + return list; +} + +QString RegExpExtractor::plainString(bool keepPos) +{ + QString tmp=_string; + + MatchedEntryInfo *ti; + for(ti=_matches.first(); ti != 0; ti=_matches.next()) + { + uint len=ti->extracted.length(); + QString s; + for(uint i=0; i<len; i++) + { + s+=' '; + } + tmp.replace(ti->index,len,s); + + } + if(!keepPos) + { + tmp=tmp.simplifyWhiteSpace(); + } + + return tmp; +} + +QString RegExpExtractor::matchesReplaced(const QString& replace) +{ + QString tmp=_string; + + int posCorrection=0; + int replaceLen=replace.length(); + + MatchedEntryInfo *ti; + for(ti=_matches.first(); ti != 0; ti=_matches.next()) + { + uint len=ti->extracted.length(); + tmp.replace(ti->index + posCorrection,len,replace); + + posCorrection+=(replaceLen-len); + } + + return tmp; +} + +void RegExpExtractor::processString() +{ + _matches.clear(); + + // if there is no regexp to be matched, quit + if( regExpList().empty() ) return; + + QValueList<MatchedEntryInfo> tmpList; + + bool found=false; + QString tmp=_string; + do + { + found=false; + QStringList::Iterator it; + for(it=_regExpList.begin();it!=_regExpList.end();++it) + { + int pos=-1; + QString tag; + + QRegExp reg = QRegExp((*it)); + + pos = reg.search(tmp); + int len=reg.matchedLength(); + if(pos>=0) + { + tag=tmp.mid(pos,len); + } + + if(pos >= 0) + { + found=true; + + MatchedEntryInfo ti; + ti.index=pos; + ti.extracted=tag; + tmpList.append(ti); + + QString s; + for(uint i=0; i<tag.length(); i++) + { + s+=' '; + } + tmp.replace(pos,tag.length(),s); + + break; + } + } + } + while( found ); + + uint num=tmpList.count(); + + for(uint i=0; i < num; i++) + { + uint n= 0; + uint min=_string.length(); + uint counter=0; + QValueList<MatchedEntryInfo>::Iterator it; + for(it=tmpList.begin();it!=tmpList.end();++it) + { + if((*it).index < min) + { + min=(*it).index; + n=counter; + } + + counter++; + } + + it=tmpList.at(n); + MatchedEntryInfo *ti = new MatchedEntryInfo; + ti->index=(*it).index; + ti->extracted=(*it).extracted; + _matches.append(ti); + tmpList.remove(it); + } +} + +QStringList RegExpExtractor::regExpList() +{ + return _regExpList; +} + +void RegExpExtractor::setRegExpList( const QStringList& regexps ) +{ + _regExpList = regexps; +} + +void RegExpExtractor::addRegExpIdentifier(QString regExp) +{ + _regExpList.append(regExp); +} + +void RegExpExtractor::deleteRegExpIdentifier(QString regExp) +{ + _regExpList.remove(regExp); +} + diff --git a/kbabel/common/regexpextractor.h b/kbabel/common/regexpextractor.h new file mode 100644 index 00000000..89703fba --- /dev/null +++ b/kbabel/common/regexpextractor.h @@ -0,0 +1,158 @@ +/* **************************************************************************** + This file is part of KBabel + + Copyright (C) 2001 by Matthias Kiefer <matthias.kiefer@gmx.de> + + based on code of Andrea Rizzi <rizzi@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the 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. + +**************************************************************************** */ +#ifndef _REGEXP_EXTRACTOR_H_ +#define _REGEXP_EXTRACTOR_H_ + +#include <qptrlist.h> +#include <qstring.h> +#include <qstringlist.h> +#include <qvaluelist.h> +#include <qregexp.h> +#include <kdemacros.h> + +namespace KBabel +{ + +struct KDE_EXPORT MatchedEntryInfo{ + uint index; + QString extracted; +}; + + +/** + * class to extract entries based on regexps from a string + * @author Andrea Rizzi <rizzi@kde.org> + */ +class KDE_EXPORT RegExpExtractor +{ + +public: + /** + * Create a regexp extractor for "string" + */ + RegExpExtractor(const QStringList& regexps); + + virtual ~RegExpExtractor() {} + + /** + * Set the string of this extractor + */ + void setString(QString string); + + /** + * @return the number of matches found + */ + uint countMatches(); + + /** + * @return the first match, and set the internal cursor to + * the beginning + */ + QString firstMatch(); + + /** + * @return the next match and move cursor forward + */ + QString nextMatch(); + + /** + * @return the n-th match. It also moves the cursor. + */ + QString match(uint matchnumber); + + /** + * @return the n-th match. It also moves the cursor. -1 is there + * is no such match. + */ + int matchIndex(uint matchnumber); + + /** + * @return the next match and move cursor forward + */ + QString prevMatch(); + + /** + * @return the last match and move the cursor to the end + */ + QString lastMatch(); + + /** + * @return a list of all matches + */ + QStringList matches(); + + /** + * @return the string without matched text + * + * @param keepPos if false, the matches are just removed and so the + * position of the other words in the string will change. If true, + * the matches are replaced with ' ' and therefore the position of the + * words will not change + */ + QString plainString(bool keepPos=false); + + /** + * @return the string, where matches are replaced with the given string + */ + QString matchesReplaced(const QString& replace); + + //Functions that allow user to define his own regexps. + + /** + * Add a regexp to the list of regexp identifier. + */ + void addRegExpIdentifier(QString regexp); + + + /** + * Delete from the regexp list the regexp. + */ + void deleteRegExpIdentifier(QString regexp); + + void setRegExpList( const QStringList& regexps ); + + QStringList regExpList(); + +protected: + + void processString(); + QPtrList<MatchedEntryInfo> _matches; + QString _string; + QStringList _regExpList; +}; + + +} + +#endif diff --git a/kbabel/common/resources.h b/kbabel/common/resources.h new file mode 100644 index 00000000..d6054b43 --- /dev/null +++ b/kbabel/common/resources.h @@ -0,0 +1,43 @@ +/* + This file is part of KBabel + + Copyright (c) 2005 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., 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. + +*/ + +#ifndef RESOURCES_H +#define RESOURCES_H + +#include <kdebug.h> +#define KBABEL 8107 +#define KBABEL_SEARCH 8108 +#define KBABEL_CATMAN 8109 + + +#endif // RESOURCES_H + diff --git a/kbabel/common/stringdistance.cpp b/kbabel/common/stringdistance.cpp new file mode 100644 index 00000000..32cb9b96 --- /dev/null +++ b/kbabel/common/stringdistance.cpp @@ -0,0 +1,182 @@ +/* **************************************************************************** + Copyright (C) 2003-2004 Eva Brucherseifer <eva.brucherseifer@basyskom.com> + 2005 Stanislav visnovsky <visnovsky@kde.org> + + This file is part of the KDE project + + 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 "stringdistance.h" + +using namespace std; + +//! Debug-Messages 0 : off 1 : a few 10 : more +int Distance::debug = 0; +const int Distance::editCost_replace_base = 1; + +const int HammingDistance::editCost = 1; + +const int LevenshteinDistance::editCost_replace = 1; +const int LevenshteinDistance::editCost_insert = 1; +const int LevenshteinDistance::editCost_delete = 1; + + +double relativeDistance(double distance, const QString& left_string, const QString& right_string) +{ + double maxsize=0; + double compsize=0; + maxsize = left_string.length(); + compsize=right_string.length(); + if (compsize>maxsize) + maxsize=compsize; + return distance/(double)maxsize; +} + + +/** This function walk trough the treeS(left & right) at the same time. + * There are in both entities the same number of trees! + * This function sums all the distances between all trees. + * For the calculation of the distance between two trees, it calls the function calculate. + */ +double Distance::operator()(const QString& left_string, const QString& right_string) +{ + m_distance = 0.00; + if (left_string == right_string) + return 0.00; // saves calculation time, both entities are the same + + // swap strings, our matrix requires that + if (left_string.length () < right_string.length() ) + { + m_distance = calculate(right_string, left_string); + } + else + { + m_distance = calculate(left_string, right_string); + } + +// if (debug > 0) cout << " --> total distance: " << m_distance << endl; + return m_distance; +} + +/** This function calculates the distance between two nodes. + * For the calculation you can specify two variables gap & distance. + */ +int Distance::nodeDistance(const QString& left_letter, const QString& right_letter) +{ + if ( left_letter == right_letter ) + { +// if (debug > 0) cout << "."; + return 0; + } + else + { +// if (debug > 0) cout << "!"; + return editCostReplace(); + } +} + +/** This function walks along the treeS(left & right) at the same time. + * There are in both entities the same number of nodes, hopefully! But it doesn't care. + * This function sums all the distances between all nodes. + * For the calculation you can specify the distance between two nodes in variable distance + */ +double HammingDistance::calculate(const QString& left_string, const QString& right_string) +{ + double hammingDistance = 0.00; +// if (debug > 0) +// cout << left_string.length() << " " << right_string.length() << "\t"; + + unsigned int i=0; + unsigned int j=0; + for ( ; i != left_string.length() && j != right_string.length() ; + ++i,++j) + hammingDistance += double(nodeDistance(left_string[i], right_string[i])); + for ( ; i != left_string.length() ; ++i ) + { + ++hammingDistance; +// if (debug > 9) cout << "!"; + } + for ( ; j != right_string.length() ; ++j) + { + ++hammingDistance; +// if (debug > 9) cout << "!"; + } + return hammingDistance; +} + + +/** This function walk along the treeS(left & right) at the same time. + * It uses the Levenshtein-algorithm for the calculation of the distance between two trees. + * A matrice D is generated which represent the distribution of distances between two trees. + * The last element represent the Levenshtein-distance. + */ +double LevenshteinDistance::calculate(const QString& left_string, const QString& right_string) +{ +// if (debug > 0) +// cout << left_string.length() << " " << right_string.length() << "\t"; + + unsigned int left_size = left_string.length()+1; + unsigned int right_size = right_string.length()+1; + + int *_D = new int[left_size * right_size]; + + for (unsigned int i = 0 ; i < left_size * right_size; i++ ) + _D[i] = 0; + +#define D(a,b) (_D[(a)+(b)*left_size]) + + +// boost::numeric::ublas::matrix<int> D(left_size, right_size); +// D = zero_boost::numeric::ublas::matrix<int>(left_size, right_size); + + unsigned int l,r; + D(0,0) = 0; + for(l = 1; l < left_size; l++) + D(l,0) = D(l-1,0) + editCost_delete; + for(r = 1; r < right_size; r++) + D(0,r) = D(0,r-1) + editCost_insert; + + int tmp_value; + for(l = 1; l < left_size; l++) + { + for(r = 1; r < right_size; r++) + { + tmp_value = QMIN( ( D(l-1,r) + editCost_delete ), + ( D(l-1,r-1) + nodeDistance(left_string[l-1], right_string[r-1]) ) ) ; + D(l,r) = QMIN( tmp_value, + ( D(l,r-1) + editCost_insert ) ); + } + } + + double res (D(left_size-1,right_size-1)); + + delete[] _D; + + return res; +} + diff --git a/kbabel/common/stringdistance.h b/kbabel/common/stringdistance.h new file mode 100644 index 00000000..6f5aa185 --- /dev/null +++ b/kbabel/common/stringdistance.h @@ -0,0 +1,131 @@ +/* **************************************************************************** + Copyright (C) 2003-2004 Eva Brucherseifer <eva.brucherseifer@basyskom.com> + 2005 Stanislav Visnovsky <visnovsky@kde.org> + + This file is part of the KDE project + + 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. + +**************************************************************************** */ + +#ifndef STRINGDISTANCE_H +#define STRINGDISTANCE_H + +#include <qstring.h> + +//#include <boost/numeric/ublas/matrix.hpp> + + +/** Private copy constructor and copy assignment ensure classes derived from + * class noncopyable cannot be copied. + * Taken from Boost library + * Contributed by Dave Abrahams + * + * If anyone needs a namespace here, please tell me at eva@rt.e-technik.tu-darmstadt.de + */ +class NonCopyable +{ +protected: + NonCopyable(){} + virtual ~NonCopyable(){} +private: // emphasize the following members are private + NonCopyable( const NonCopyable& ); + const NonCopyable& operator=( const NonCopyable& ); +}; + + +/** + * @class Distance + * @author Eva Brucherseifer + * + * The class Distance calculates the distance + * between two Entities (left & right). + * It is the parent for other distance-classes. + */ +class Distance : public NonCopyable +{ +public: + virtual ~Distance(){} + double operator()(const QString& left, const QString& right); + + int editCostReplace() { return editCost_replace_base; } + static int debug; + +protected: + virtual double calculate(const QString& left_string, const QString& right_string) = 0; + int nodeDistance(const QString& left_letter, const QString& right_letter); + static const int editCost_replace_base; + double m_distance; +}; + + +double relativeDistance(double distance, const QString& left_string, const QString right_string); + + +/** + * @class HammingDistance + * @author Eva Brucherseifer + * + * The class HammingDistance is based on an algorithm + * of Hamming. It increase the distance if the nodes from + * the tree are not the same. Also called edit-distance. + */ +class HammingDistance : public Distance +{ +protected: + virtual double calculate(const QString& left_string, const QString& right_string); + int editCostReplace() { return editCost; } + static const int editCost; +}; + +/** + * @class LevenshteinDistance + * @author Eva Brucherseifer + * + * The class LevenshteinDistance is based on an algorithm + * of Levenshtein. It search for the minimum distance of + * two trees. You can specify the distance between + * a gap & a node and between two different nodes. + */ +class LevenshteinDistance : public Distance +{ +protected: + virtual double calculate(const QString& left_string, const QString& right_string); + int editCostReplace() { return editCost_replace; } + static const int editCost_replace; + static const int editCost_insert; + static const int editCost_delete; +}; + + +/** wrapper function for replacement of fstrcmp from gettext */ +inline double fstrcmp(const QString& left, const QString& right) +{ + return LevenshteinDistance()(left,right); +} + + +#endif diff --git a/kbabel/common/tagextractor.cpp b/kbabel/common/tagextractor.cpp new file mode 100644 index 00000000..45b8ad9f --- /dev/null +++ b/kbabel/common/tagextractor.cpp @@ -0,0 +1,54 @@ +/* **************************************************************************** + This file is part of KBabel + + Copyright (C) 2001 by Matthias Kiefer <matthias.kiefer@gmx.de> + 2002 by Stanislav Visnovsky <visnovsky@nenya.ms.mff.cuni.cz> + + based on code of Andrea Rizzi <rizzi@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the 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 "catalogsettings.h" +#include "tagextractor.h" + +#include <kglobal.h> +#include <kconfig.h> + +using namespace KBabel; + +TagExtractor::TagExtractor() : RegExpExtractor(QStringList()) +{ + KConfig* config = KGlobal::config(); + + config->setGroup("Tags"); + + QStringList s=config->readListEntry("TagExpressions"); + if( s.empty() ) s = Defaults::Tag::tagExpressions(); + + RegExpExtractor::setRegExpList(s); +} + diff --git a/kbabel/common/tagextractor.h b/kbabel/common/tagextractor.h new file mode 100644 index 00000000..3845b9fd --- /dev/null +++ b/kbabel/common/tagextractor.h @@ -0,0 +1,58 @@ +/* **************************************************************************** + This file is part of KBabel + + Copyright (C) 2001 by Matthias Kiefer <matthias.kiefer@gmx.de> + + based on code of Andrea Rizzi <rizzi@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the 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. + +**************************************************************************** */ +#ifndef _TAG_EXTRACTOR_H_ +#define _TAG_EXTRACTOR_H_ + +#include "regexpextractor.h" + +namespace KBabel +{ + +/** + * class to extract tags from a string + * @author Andrea Rizzi <rizzi@kde.org> + */ +class KDE_EXPORT TagExtractor : public RegExpExtractor +{ + +public: + /** + * Create a tag extractor by using default settings for tag expressions + */ + TagExtractor(); +}; + +} + +#endif |