diff --git a/src/alttransview.cpp b/src/alttransview.cpp
index f351a4e..a1089be 100644
--- a/src/alttransview.cpp
+++ b/src/alttransview.cpp
@@ -1,330 +1,328 @@
/* ****************************************************************************
This file is part of Lokalize
Copyright (C) 2007-2014 by Nick Shaforostoff
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) version 3 or any later version
accepted by the membership of KDE e.V. (or its successor approved
by the membership of KDE e.V.), which shall act as a proxy
defined in Section 14 of version 3 of the license.
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, see .
**************************************************************************** */
#include "alttransview.h"
#include "lokalize_debug.h"
#include "diff.h"
#include "catalog.h"
#include "cmd.h"
#include "project.h"
#include "xlifftextedit.h"
#include "tmview.h" //TextBrowser
#include "mergecatalog.h"
#include "prefs_lokalize.h"
-#include "kdemacros.h"
-
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
AltTransView::AltTransView(QWidget* parent, Catalog* catalog,const QVector& actions)
: QDockWidget ( i18nc("@title:window","Alternate Translations"), parent)
, m_browser(new TM::TextBrowser(this))
, m_catalog(catalog)
, m_normTitle(i18nc("@title:window","Alternate Translations"))
, m_hasInfoTitle(m_normTitle+QStringLiteral(" [*]"))
, m_hasInfo(false)
, m_everShown(false)
, m_actions(actions)
{
setObjectName(QStringLiteral("msgIdDiff"));
setWidget(m_browser);
hide();
m_browser->setReadOnly(true);
m_browser->viewport()->setBackgroundRole(QPalette::Background);
QTimer::singleShot(0,this,SLOT(initLater()));
}
void AltTransView::initLater()
{
setAcceptDrops(true);
#ifndef NOKDE
KConfig config;
KConfigGroup group(&config,"AltTransView");
m_everShown=group.readEntry("EverShown",false);
#endif
QSignalMapper* signalMapper=new QSignalMapper(this);
int i=m_actions.size();
while(--i>=0)
{
connect(m_actions.at(i),SIGNAL(triggered()),signalMapper,SLOT(map()));
signalMapper->setMapping(m_actions.at(i), i);
}
connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(slotUseSuggestion(int)));
connect(m_browser,SIGNAL(textInsertRequested(QString)),this,SIGNAL(textInsertRequested(QString)));
//connect(m_browser,SIGNAL(customContextMenuRequested(QPoint)),this,SLOT(contextMenu(QPoint)));
}
AltTransView::~AltTransView()
{
}
void AltTransView::dragEnterEvent(QDragEnterEvent* event)
{
if(event->mimeData()->hasUrls() && Catalog::extIsSupported(event->mimeData()->urls().first().path()))
event->acceptProposedAction();
}
void AltTransView::dropEvent(QDropEvent *event)
{
event->acceptProposedAction();
attachAltTransFile(event->mimeData()->urls().first().toLocalFile());
//update
m_prevEntry.entry=-1;
QTimer::singleShot(0,this,SLOT(process()));
}
void AltTransView::attachAltTransFile(const QString& path)
{
MergeCatalog* altCat=new MergeCatalog(m_catalog, m_catalog, /*saveChanges*/false);
altCat->loadFromUrl(path);
m_catalog->attachAltTransCatalog(altCat);
}
void AltTransView::addAlternateTranslation(int entry, const QString& trans, bool temp)
{
AltTrans altTrans;
altTrans.target=trans;
m_catalog->attachAltTrans(entry, altTrans);
m_prevEntry=DocPos();
QTimer::singleShot(0,this,SLOT(process()));
}
void AltTransView::fileLoaded()
{
m_prevEntry.entry=-1;
QString absPath=m_catalog->url();
QString relPath=QDir(Project::instance()->projectDir()).relativeFilePath(absPath);
QFileInfo info(Project::instance()->altTransDir()%'/'%relPath);
if (info.canonicalFilePath()!=absPath && info.exists())
attachAltTransFile(info.canonicalFilePath());
else
qCWarning(LOKALIZE_LOG)<<"alt trans file doesn't exist:"<altTransDir()%'/'%relPath;
}
void AltTransView::slotNewEntryDisplayed(const DocPosition& pos)
{
m_entry=DocPos(pos);
QTimer::singleShot(0,this,SLOT(process()));
}
void AltTransView::process()
{
if (m_entry==m_prevEntry) return;
if (m_catalog->numberOfEntries()<=m_entry.entry)
return;//because of Qt::QueuedConnection
m_prevEntry=m_entry;
m_browser->clear();
m_entryPositions.clear();
const QVector& entries=m_catalog->altTrans(m_entry.toDocPosition());
m_entries=entries;
if (entries.isEmpty())
{
if (m_hasInfo)
{
m_hasInfo=false;
setWindowTitle(m_normTitle);
}
return;
}
if (!m_hasInfo)
{
m_hasInfo=true;
setWindowTitle(m_hasInfoTitle);
}
if(!isVisible() && !Settings::altTransViewEverShownWithData())
{
if (KMessageBox::questionYesNo(this,i18n("There is useful data available in Alternate Translations view.\n\n"
"For Gettext PO files it displays difference between current source text "
"and the source text corresponding to the fuzzy translation found by msgmerge when updating PO based on POT template.\n\n"
"Do you want to show the view with the data?"), m_normTitle)==KMessageBox::Yes)
show();
Settings::setAltTransViewEverShownWithData(true);
}
CatalogString source=m_catalog->sourceWithTags(m_entry.toDocPosition());
QTextBlockFormat blockFormatBase;
QTextBlockFormat blockFormatAlternate; blockFormatAlternate.setBackground(QPalette().alternateBase());
QTextCharFormat noncloseMatchCharFormat;
QTextCharFormat closeMatchCharFormat; closeMatchCharFormat.setFontWeight(QFont::Bold);
int i=0;
int limit=entries.size();
forever
{
const AltTrans& entry=entries.at(i);
QTextCursor cur=m_browser->textCursor();
QString html;
html.reserve(1024);
if (!entry.source.isEmpty())
{
html+=QStringLiteral("");
QString result=userVisibleWordDiff(entry.source.string, source.string,Project::instance()->accel(),Project::instance()->markup()).toHtmlEscaped();
//result.replace("&","&");
//result.replace("<","<");
//result.replace(">",">");
result.replace(QStringLiteral("{KBABELADD}"),QStringLiteral(""));
result.replace(QStringLiteral("{/KBABELADD}"),QStringLiteral(" "));
result.replace(QStringLiteral("{KBABELDEL}"),QStringLiteral(""));
result.replace(QStringLiteral("{/KBABELDEL}"),QStringLiteral(" "));
result.replace(QStringLiteral("\\n"),QStringLiteral("\\n "));
html+=result;
html+=QStringLiteral(" ");
cur.insertHtml(html); html.clear();
}
if (!entry.target.isEmpty())
{
- if (KDE_ISLIKELY( isetStatusTip(entry.target.string);
html+=QString(QStringLiteral("[%1] ")).arg(m_actions.at(i)->shortcut().toString(QKeySequence::NativeText));
}
else
html+=QStringLiteral("[ - ] ");
cur.insertText(html); html.clear();
insertContent(cur,entry.target);
}
m_entryPositions.insert(cur.anchor(),i);
html+=i?QStringLiteral("
"):QStringLiteral("
");
cur.insertHtml(html);
- if (KDE_ISUNLIKELY( ++i>=limit ))
+ if (Q_UNLIKELY( ++i>=limit ))
break;
cur.insertBlock(i%2?blockFormatAlternate:blockFormatBase);
}
#ifndef NOKDE
if (!m_everShown)
{
m_everShown=true;
show();
KConfig config;
KConfigGroup group(&config,"AltTransView");
group.writeEntry("EverShown",true);
}
#endif
}
bool AltTransView::event(QEvent *event)
{
if (event->type()==QEvent::ToolTip)
{
QHelpEvent *helpEvent = static_cast(event);
if (m_entryPositions.isEmpty())
{
QString tooltip=i18nc("@info:tooltip","Sometimes, if source text is changed, its translation becomes deprecated and is either marked as needing review (i.e. looses approval status), "
"or (only in case of XLIFF file) moved to the alternate translations section accompanying the unit.
"
"This toolview also shows the difference between current source string and the previous source string, so that you can easily see which changes should be applied to existing translation to make it reflect current source.
"
"Double-clicking any word in this toolview inserts it into translation.
"
"Drop translation file onto this toolview to use it as a source for additional alternate translations.
"
);
QToolTip::showText(helpEvent->globalPos(),tooltip);
return true;
}
int block1=m_browser->cursorForPosition(m_browser->viewport()->mapFromGlobal(helpEvent->globalPos())).blockNumber();
int block=*m_entryPositions.lowerBound(m_browser->cursorForPosition(m_browser->viewport()->mapFromGlobal(helpEvent->globalPos())).anchor());
if (block1!=block)
qCWarning(LOKALIZE_LOG)<<"block numbers don't match";
if (block>=m_entries.size())
return false;
QString origin=m_entries.at(block).origin;
if (origin.isEmpty())
return false;
QString tooltip=i18nc("@info:tooltip","Origin: %1",origin);
QToolTip::showText(helpEvent->globalPos(),tooltip);
return true;
}
return QWidget::event(event);
}
void AltTransView::slotUseSuggestion(int i)
{
- if (KDE_ISUNLIKELY( i>=m_entries.size() ))
+ if (Q_UNLIKELY( i>=m_entries.size() ))
return;
TM::TMEntry tmEntry;
tmEntry.target=m_entries.at(i).target;
CatalogString source=m_catalog->sourceWithTags(m_entry.toDocPosition());
tmEntry.diff=userVisibleWordDiff(m_entries.at(i).source.string, source.string,Project::instance()->accel(),Project::instance()->markup());
CatalogString target=TM::targetAdapted(tmEntry, source);
qCWarning(LOKALIZE_LOG)<<"0"<beginMacro(i18nc("@item Undo action","Use alternate translation"));
QString old=m_catalog->targetWithTags(m_entry.toDocPosition()).string;
if (!old.isEmpty())
{
//FIXME test!
removeTargetSubstring(m_catalog, m_entry.toDocPosition(), 0, old.size());
//m_catalog->push(new DelTextCmd(m_catalog,m_pos,m_catalog->msgstr(m_pos)));
}
qCWarning(LOKALIZE_LOG)<<"1"<push(new InsTextCmd(m_catalog,m_pos,target)/*,true*/);
insertCatalogString(m_catalog, m_entry.toDocPosition(), target, 0);
m_catalog->endMacro();
emit refreshRequested();
}
diff --git a/src/catalog/catalog.cpp b/src/catalog/catalog.cpp
index f19385f..0ba14da 100644
--- a/src/catalog/catalog.cpp
+++ b/src/catalog/catalog.cpp
@@ -1,1084 +1,1084 @@
/* ****************************************************************************
This file is part of Lokalize
This file contains parts of KBabel code
Copyright (C) 1999-2000 by Matthias Kiefer
2001-2005 by Stanislav Visnovsky
2006 by Nicolas Goutte
2007-2014 by Nick Shaforostoff
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 "catalog.h"
#include "catalog_private.h"
#include "project.h"
#ifndef NOKDE
#include "projectmodel.h" //to notify about modification
#endif
#include "catalogstorage.h"
#include "gettextstorage.h"
#include "gettextimport.h"
#include "gettextexport.h"
#include "xliffstorage.h"
#include "tsstorage.h"
#include "mergecatalog.h"
#include "version.h"
#include "prefs_lokalize.h"
#include "jobs.h"
#include "dbfilesmodel.h"
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef Q_OS_WIN
#define U QLatin1String
#else
#define U QStringLiteral
#endif
//QString Catalog::supportedMimeFilters("text/x-gettext-translation application/x-xliff application/x-linguist"); //" text/x-gettext-translation-template")
QString Catalog::supportedFileTypes(bool includeTemplates)
{
QString sep=QStringLiteral(";;");
QString all=i18n("All supported files (*.po *.pot *.xlf *.xliff *.ts)")+sep;
return all+(includeTemplates ? i18n("Gettext (*.po *.pot)") : i18n("Gettext (*.po)"))+sep+i18n("XLIFF (*.xlf *.xliff)")+sep+i18n("Linguist (*.ts)");
}
static const QString extensions[]={U(".po"),U(".pot"),U(".xlf"),U(".xliff"),U(".ts")};
static const char* const xliff_states[]={
I18N_NOOP("New"),I18N_NOOP("Needs translation"),I18N_NOOP("Needs full localization"),I18N_NOOP("Needs adaptation"),I18N_NOOP("Translated"),
I18N_NOOP("Needs translation review"),I18N_NOOP("Needs full localization review"),I18N_NOOP("Needs adaptation review"),I18N_NOOP("Final"),
I18N_NOOP("Signed-off")};
const char* const* Catalog::states()
{
return xliff_states;
}
QStringList Catalog::supportedExtensions()
{
QStringList result;
int i=sizeof(extensions)/sizeof(QString);
while (--i>=0)
result.append(extensions[i]);
return result;
}
bool Catalog::extIsSupported(const QString& path)
{
QStringList ext=supportedExtensions();
int i=ext.size();
while (--i>=0 && !path.endsWith(ext.at(i)))
;
return i!=-1;
}
Catalog::Catalog(QObject *parent)
: QUndoStack(parent)
, d(this)
, m_storage(0)
{
#ifndef NOKDE
//cause refresh events for files modified from lokalize itself aint delivered automatically
connect(this,SIGNAL(signalFileSaved(QString)),
Project::instance()->model(),SLOT(slotFileSaved(QString)),Qt::QueuedConnection);
QTimer* t=&(d._autoSaveTimer);
t->setInterval(2*60*1000);
t->setSingleShot(false);
connect(t, &QTimer::timeout, this, &Catalog::doAutoSave);
connect(this,SIGNAL(signalFileSaved()), t,SLOT(start()));
connect(this,SIGNAL(signalFileLoaded()), t,SLOT(start()));
connect(this, &Catalog::indexChanged, this, &Catalog::setAutoSaveDirty);
#endif
connect(Project::local(),SIGNAL(configChanged()),this,SLOT(projectConfigChanged()));
}
Catalog::~Catalog()
{
clear();
//delete m_storage; //deleted in clear();
}
void Catalog::clear()
{
setIndex(cleanIndex());//to keep TM in sync
QUndoStack::clear();
d._errorIndex.clear();
d._nonApprovedIndex.clear();
d._emptyIndex.clear();
delete m_storage;m_storage=0;
d._filePath.clear();
d._lastModifiedPos=DocPosition();
d._modifiedEntries.clear();
while (!d._altTransCatalogs.isEmpty())
d._altTransCatalogs.takeFirst()->deleteLater();
d._altTranslations.clear();
/*
d.msgidDiffList.clear();
d.msgstr2MsgidDiffList.clear();
d.diffCache.clear();
*/
}
void Catalog::push(QUndoCommand* cmd)
{
generatePhaseForCatalogIfNeeded(this);
QUndoStack::push(cmd);
}
//BEGIN STORAGE TRANSLATION
int Catalog::capabilities() const
{
if (Q_UNLIKELY( !m_storage )) return 0;
return m_storage->capabilities();
}
int Catalog::numberOfEntries() const
{
if (Q_UNLIKELY( !m_storage )) return 0;
return m_storage->size();
}
static DocPosition alterForSinglePlural(const Catalog* th, DocPosition pos)
{
//if source lang is english (implied) and target lang has only 1 plural form (e.g. Chinese)
if (Q_UNLIKELY(th->numberOfPluralForms()==1 && th->isPlural(pos)))
pos.form=1;
return pos;
}
QString Catalog::msgid(const DocPosition& pos) const
{
if (Q_UNLIKELY( !m_storage ))
return QString();
return m_storage->source(alterForSinglePlural(this, pos));
}
QString Catalog::msgstr(const DocPosition& pos) const
{
if (Q_UNLIKELY( !m_storage ))
return QString();
return m_storage->target(pos);
}
CatalogString Catalog::sourceWithTags(const DocPosition& pos) const
{
if (Q_UNLIKELY( !m_storage ))
return CatalogString();
return m_storage->sourceWithTags(alterForSinglePlural(this, pos));
}
CatalogString Catalog::targetWithTags(const DocPosition& pos) const
{
if (Q_UNLIKELY( !m_storage ))
return CatalogString();
return m_storage->targetWithTags(pos);
}
CatalogString Catalog::catalogString(const DocPosition& pos) const
{
if (Q_UNLIKELY( !m_storage ))
return CatalogString();
return m_storage->catalogString(pos.part==DocPosition::Source?alterForSinglePlural(this, pos):pos);
}
QVector Catalog::notes(const DocPosition& pos) const
{
if (Q_UNLIKELY( !m_storage ))
return QVector();
return m_storage->notes(pos);
}
QVector Catalog::developerNotes(const DocPosition& pos) const
{
if (Q_UNLIKELY( !m_storage ))
return QVector();
return m_storage->developerNotes(pos);
}
Note Catalog::setNote(const DocPosition& pos, const Note& note)
{
if (Q_UNLIKELY( !m_storage ))
return Note();
return m_storage->setNote(pos,note);
}
QStringList Catalog::noteAuthors() const
{
if (Q_UNLIKELY( !m_storage ))
return QStringList();
return m_storage->noteAuthors();
}
void Catalog::attachAltTransCatalog(Catalog* altCat)
{
d._altTransCatalogs.append(altCat);
if (numberOfEntries()!=altCat->numberOfEntries())
qCWarning(LOKALIZE_LOG)<url()<<"has different number of entries";
}
void Catalog::attachAltTrans(int entry, const AltTrans& trans)
{
d._altTranslations.insert(entry, trans);
}
QVector Catalog::altTrans(const DocPosition& pos) const
{
QVector result;
if (m_storage)
result=m_storage->altTrans(pos);
foreach(Catalog* altCat, d._altTransCatalogs)
{
if (pos.entry>=altCat->numberOfEntries())
{
qCDebug(LOKALIZE_LOG)<<"ignoring"<url()<<"this time because"<numberOfEntries();
continue;
}
if (altCat->source(pos)!=source(pos))
{
qCDebug(LOKALIZE_LOG)<<"ignoring"<url()<<"this time because s don't match";
continue;
}
QString target=altCat->msgstr(pos);
if (!target.isEmpty() && altCat->isApproved(pos))
{
result<url();
}
}
if (d._altTranslations.contains(pos.entry))
result<sourceFiles(pos);
}
QString Catalog::id(const DocPosition& pos) const
{
if (Q_UNLIKELY( !m_storage ))
return QString();
return m_storage->id(pos);
}
QStringList Catalog::context(const DocPosition& pos) const
{
if (Q_UNLIKELY( !m_storage ))
return QStringList();
return m_storage->context(pos);
}
QString Catalog::setPhase(const DocPosition& pos, const QString& phase)
{
if (Q_UNLIKELY( !m_storage ))
return QString();
return m_storage->setPhase(pos,phase);
}
void Catalog::setActivePhase(const QString& phase, ProjectLocal::PersonRole role)
{
//qCDebug(LOKALIZE_LOG)<<"setting active phase"<size();
while (pos.entryisEmpty(pos))
d._emptyIndex << pos.entry;
++(pos.entry);
}
emit signalNumberOfFuzziesChanged();
emit signalNumberOfEmptyChanged();
}
QString Catalog::phase(const DocPosition& pos) const
{
if (Q_UNLIKELY( !m_storage ))
return QString();
return m_storage->phase(pos);
}
Phase Catalog::phase(const QString& name) const
{
return m_storage->phase(name);
}
QList Catalog::allPhases() const
{
return m_storage->allPhases();
}
QVector Catalog::phaseNotes(const QString& phase) const
{
return m_storage->phaseNotes(phase);
}
QVector Catalog::setPhaseNotes(const QString& phase, QVector notes)
{
return m_storage->setPhaseNotes(phase, notes);
}
QMap Catalog::allTools() const
{
return m_storage->allTools();
}
bool Catalog::isPlural(uint index) const
{
return m_storage && m_storage->isPlural(DocPosition(index));
}
bool Catalog::isApproved(uint index) const
{
if (Q_UNLIKELY( !m_storage ))
return false;
bool extendedStates=m_storage->capabilities()&ExtendedStates;
return (extendedStates&&::isApproved(state(DocPosition(index)),activePhaseRole()))
||(!extendedStates&&m_storage->isApproved(DocPosition(index)));
}
TargetState Catalog::state(const DocPosition& pos) const
{
if (Q_UNLIKELY( !m_storage ))
return NeedsTranslation;
if (m_storage->capabilities()&ExtendedStates)
return m_storage->state(pos);
else
return closestState(m_storage->isApproved(pos), activePhaseRole());
}
bool Catalog::isEmpty(uint index) const
{
return m_storage && m_storage->isEmpty(DocPosition(index));
}
bool Catalog::isEmpty(const DocPosition& pos) const
{
return m_storage && m_storage->isEmpty(pos);
}
bool Catalog::isEquivTrans(const DocPosition& pos) const
{
return m_storage && m_storage->isEquivTrans(pos);
}
int Catalog::binUnitsCount() const
{
return m_storage?m_storage->binUnitsCount():0;
}
int Catalog::unitById(const QString& id) const
{
return m_storage?m_storage->unitById(id):0;
}
QString Catalog::mimetype()
{
if (Q_UNLIKELY( !m_storage ))
return QString();
return m_storage->mimetype();
}
QString Catalog::fileType()
{
if (Q_UNLIKELY( !m_storage ))
return QString();
return m_storage->fileType();
}
CatalogType Catalog::type()
{
if (Q_UNLIKELY( !m_storage ))
return Gettext;
return m_storage->type();
}
QString Catalog::sourceLangCode() const
{
if (Q_UNLIKELY( !m_storage ))
return QString();
return m_storage->sourceLangCode();
}
QString Catalog::targetLangCode() const
{
if (Q_UNLIKELY( !m_storage ))
return QString();
return m_storage->targetLangCode();
}
void Catalog::setTargetLangCode(const QString& targetLangCode)
{
if (Q_UNLIKELY( !m_storage ))
return;
bool notify = m_storage->targetLangCode()!=targetLangCode;
m_storage->setTargetLangCode(targetLangCode);
if (notify) emit signalFileLoaded();
}
//END STORAGE TRANSLATION
//BEGIN OPEN/SAVE
#define DOESNTEXIST -1
#define ISNTREADABLE -2
#define UNKNOWNFORMAT -3
KAutoSaveFile* Catalog::checkAutoSave(const QString& url)
{
#ifndef NOKDE
KAutoSaveFile* autoSave=0;
QList staleFiles = KAutoSaveFile::staleFiles(QUrl::fromLocalFile(url));
foreach (KAutoSaveFile *stale, staleFiles)
{
if (stale->open(QIODevice::ReadOnly) && !autoSave)
{
autoSave=stale;
autoSave->setParent(this);
}
else
stale->deleteLater();
}
if (autoSave)
qCInfo(LOKALIZE_LOG)<<"autoSave"<fileName();
return autoSave;
#else
return 0;
#endif
}
int Catalog::loadFromUrl(const QString& filePath, const QString& saidUrl, int* fileSize, bool fast)
{
QFileInfo info(filePath);
if(Q_UNLIKELY( !info.exists() || info.isDir()) )
return DOESNTEXIST;
if(Q_UNLIKELY( !info.isReadable() ))
return ISNTREADABLE;
bool readOnly=!info.isWritable();
QTime a;a.start();
QFile file(filePath);
if (!file.open(QIODevice::ReadOnly))
return ISNTREADABLE;//TODO
CatalogStorage* storage=0;
if (filePath.endsWith(QLatin1String(".po"))||filePath.endsWith(QLatin1String(".pot")))
storage=new GettextCatalog::GettextStorage;
else if (filePath.endsWith(QLatin1String(".xlf"))||filePath.endsWith(QLatin1String(".xliff")))
storage=new XliffStorage;
else if (filePath.endsWith(QLatin1String(".ts")))
storage=new TsStorage;
else
{
//try harder
QTextStream in(&file);
int i=0;
bool gettext=false;
while (!in.atEnd()&& ++i<64 && !gettext)
gettext=in.readLine().contains(QLatin1String("msgid"));
if (gettext) storage=new GettextCatalog::GettextStorage;
else return UNKNOWNFORMAT;
}
int line=storage->load(&file);
file.close();
if (Q_UNLIKELY(line!=0 || (!storage->size() && (line==-1) ) ))
{
delete storage;
return line;
}
if (a.elapsed()>100) qCDebug(LOKALIZE_LOG)<numberOfPluralForms();
d._autoSaveDirty=true;
d._readOnly=readOnly;
d._filePath=saidUrl.isEmpty()?filePath:saidUrl;
//set some sane role, a real phase with a nmae will be created later with the first edit command
setActivePhase(QString(),Project::local()->role());
#ifndef NOKDE
if (!fast)
{
KAutoSaveFile* autoSave=checkAutoSave(d._filePath);
d._autoSaveRecovered=autoSave;
if (autoSave)
{
d._autoSave->deleteLater();
d._autoSave=autoSave;
//restore 'modified' status for entries
MergeCatalog* mergeCatalog=new MergeCatalog(this,this);
int errorLine=mergeCatalog->loadFromUrl(autoSave->fileName());
- if (KDE_ISLIKELY(errorLine==0))
+ if (Q_LIKELY(errorLine==0))
mergeCatalog->copyToBaseCatalog();
mergeCatalog->deleteLater();
d._autoSave->close();
}
else
d._autoSave->setManagedFile(QUrl::fromLocalFile(d._filePath));
}
#endif
if (fileSize)
*fileSize=file.size();
emit signalFileLoaded();
emit signalFileLoaded(d._filePath);
return 0;
}
bool Catalog::save()
{
return saveToUrl(d._filePath);
}
//this function is not called if QUndoStack::isClean() !
bool Catalog::saveToUrl(QString localFilePath)
{
if (Q_UNLIKELY( !m_storage ))
return true;
bool nameChanged=localFilePath.length();
- if (KDE_ISLIKELY( !nameChanged ))
+ if (Q_LIKELY( !nameChanged ))
localFilePath = d._filePath;
QString localPath=QFileInfo(localFilePath).absolutePath();
if (!QFileInfo::exists(localPath))
if (!QDir::root().mkpath(localPath))
return false;
QFile file(localFilePath);
if (Q_UNLIKELY( !file.open(QIODevice::WriteOnly) )) //i18n("Wasn't able to open file %1",filename.ascii());
return false;
bool belongsToProject=localFilePath.contains(Project::instance()->poDir());
if (Q_UNLIKELY( !m_storage->save(&file, belongsToProject) ))
return false;
file.close();
#ifndef NOKDE
d._autoSave->remove();
d._autoSaveRecovered=false;
#endif
setClean(); //undo/redo
if (nameChanged)
{
d._filePath=localFilePath;
#ifndef NOKDE
d._autoSave->setManagedFile(QUrl::fromLocalFile(localFilePath));
#endif
}
//Settings::self()->setCurrentGroup("Bookmarks");
//Settings::self()->addItemIntList(d._filePath.url(),d._bookmarkIndex);
emit signalFileSaved();
emit signalFileSaved(localFilePath);
return true;
/*
else if (status==NO_PERMISSIONS)
{
if (KMessageBox::warningContinueCancel(this,
i18n("You do not have permission to write to file:\n%1\n"
"Do you want to save to another file or cancel?", _currentURL.prettyUrl()),
i18n("Error"),KStandardGuiItem::save())==KMessageBox::Continue)
return fileSaveAs();
}
*/
}
void Catalog::doAutoSave()
{
#ifndef NOKDE
if (isClean()||!(d._autoSaveDirty))
return;
if (Q_UNLIKELY( !m_storage ))
return;
if (!d._autoSave->open(QIODevice::WriteOnly))
{
emit signalFileAutoSaveFailed(d._autoSave->fileName());
return;
}
qCInfo(LOKALIZE_LOG)<<"doAutoSave"<fileName();
m_storage->save(d._autoSave);
d._autoSave->close();
d._autoSaveDirty=false;
#endif
}
void Catalog::projectConfigChanged()
{
setActivePhase(activePhase(), Project::local()->role());
}
QByteArray Catalog::contents()
{
QBuffer buf;
buf.open(QIODevice::WriteOnly);
m_storage->save(&buf);
buf.close();
return buf.data();
}
//END OPEN/SAVE
/**
* helper method to keep db in a good shape :)
* called on
* 1) entry switch
* 2) automatic editing code like replace or undo/redo operation
**/
static void updateDB(
const QString& filePath,
const QString& ctxt,
const CatalogString& english,
const CatalogString& newTarget,
int form,
bool approved,
const QString& dbName
//const DocPosition&,//for back tracking
)
{
TM::UpdateJob* j=new TM::UpdateJob(filePath,ctxt,english,newTarget,form,approved,
dbName);
TM::threadPool()->start(j);
}
//BEGIN UNDO/REDO
const DocPosition& Catalog::undo()
{
QUndoStack::undo();
return d._lastModifiedPos;
}
const DocPosition& Catalog::redo()
{
QUndoStack::redo();
return d._lastModifiedPos;
}
void Catalog::flushUpdateDBBuffer()
{
if (!Settings::autoaddTM())
return;
DocPosition pos=d._lastModifiedPos;
if (pos.entry==-1 || pos.entry>=numberOfEntries())
{
//nothing to flush
//qCWarning(LOKALIZE_LOG)<<"nothing to flush or new file opened";
return;
}
QString dbName;
if (Project::instance()->targetLangCode()==targetLangCode())
{
dbName=Project::instance()->projectID();
}
else
{
dbName=sourceLangCode()%'-'%targetLangCode();
qCInfo(LOKALIZE_LOG)<<"updating"<targetLangCode()<m_configurations.contains(dbName))
{
TM::OpenDBJob* openDBJob=new TM::OpenDBJob(dbName, TM::Local, true);
connect(openDBJob,SIGNAL(done(OpenDBJob*)),TM::DBFilesModel::instance(),SLOT(updateProjectTmIndex()));
openDBJob->m_setParams=true;
openDBJob->m_tmConfig.markup=Project::instance()->markup();
openDBJob->m_tmConfig.accel=Project::instance()->accel();
openDBJob->m_tmConfig.sourceLangCode=sourceLangCode();
openDBJob->m_tmConfig.targetLangCode=targetLangCode();
TM::DBFilesModel::instance()->openDB(openDBJob);
}
}
int form=-1;
if (isPlural(pos.entry))
form=pos.form;
updateDB(url(),
context(pos.entry).first(),
sourceWithTags(pos),
targetWithTags(pos),
form,
isApproved(pos.entry),
dbName);
d._lastModifiedPos=DocPosition();
}
void Catalog::setLastModifiedPos(const DocPosition& pos)
{
if (pos.entry>=numberOfEntries()) //bin-units
return;
bool entryChanged=DocPos(d._lastModifiedPos)!=DocPos(pos);
if (entryChanged)
flushUpdateDBBuffer();
d._lastModifiedPos=pos;
}
bool CatalogPrivate::addToEmptyIndexIfAppropriate(CatalogStorage* storage, const DocPosition& pos, bool alreadyEmpty)
{
if ((!pos.offset)&&(storage->target(pos).isEmpty())&&(!alreadyEmpty))
{
insertInList(_emptyIndex,pos.entry);
return true;
}
return false;
}
void Catalog::targetDelete(const DocPosition& pos, int count)
{
if (Q_UNLIKELY( !m_storage ))
return;
bool alreadyEmpty = m_storage->isEmpty(pos);
m_storage->targetDelete(pos,count);
if (d.addToEmptyIndexIfAppropriate(m_storage,pos,alreadyEmpty))
emit signalNumberOfEmptyChanged();
emit signalEntryModified(pos);
}
bool CatalogPrivate::removeFromUntransIndexIfAppropriate(CatalogStorage* storage, const DocPosition& pos)
{
if ((!pos.offset)&&(storage->isEmpty(pos)))
{
_emptyIndex.removeAll(pos.entry);
return true;
}
return false;
}
void Catalog::targetInsert(const DocPosition& pos, const QString& arg)
{
if (Q_UNLIKELY( !m_storage ))
return;
if (d.removeFromUntransIndexIfAppropriate(m_storage,pos))
emit signalNumberOfEmptyChanged();
m_storage->targetInsert(pos,arg);
emit signalEntryModified(pos);
}
void Catalog::targetInsertTag(const DocPosition& pos, const InlineTag& tag)
{
if (Q_UNLIKELY( !m_storage ))
return;
if (d.removeFromUntransIndexIfAppropriate(m_storage,pos))
emit signalNumberOfEmptyChanged();
m_storage->targetInsertTag(pos,tag);
emit signalEntryModified(pos);
}
InlineTag Catalog::targetDeleteTag(const DocPosition& pos)
{
if (Q_UNLIKELY( !m_storage ))
return InlineTag();
bool alreadyEmpty = m_storage->isEmpty(pos);
InlineTag tag=m_storage->targetDeleteTag(pos);
if (d.addToEmptyIndexIfAppropriate(m_storage,pos,alreadyEmpty))
emit signalNumberOfEmptyChanged();
emit signalEntryModified(pos);
return tag;
}
void Catalog::setTarget(DocPosition pos, const CatalogString& s)
{
//TODO for case of markup present
m_storage->setTarget(pos, s.string);
}
TargetState Catalog::setState(const DocPosition& pos, TargetState state)
{
bool extendedStates = m_storage && m_storage->capabilities()&ExtendedStates;
bool approved=::isApproved(state,activePhaseRole());
if (Q_UNLIKELY( !m_storage
|| (extendedStates && m_storage->state(pos)==state)
|| (!extendedStates && m_storage->isApproved(pos)==approved)))
return this->state(pos);
TargetState prevState;
if (extendedStates)
{
prevState=m_storage->setState(pos,state);
d._statesIndex[prevState].removeAll(pos.entry);
insertInList(d._statesIndex[state],pos.entry);
}
else
{
prevState=closestState(!approved,activePhaseRole());
m_storage->setApproved(pos,approved);
}
if (!approved)
insertInList(d._nonApprovedIndex,pos.entry);
else
d._nonApprovedIndex.removeAll(pos.entry);
emit signalNumberOfFuzziesChanged();
emit signalEntryModified(pos);
return prevState;
}
Phase Catalog::updatePhase(const Phase& phase)
{
return m_storage->updatePhase(phase);
}
void Catalog::setEquivTrans(const DocPosition& pos, bool equivTrans)
{
if (m_storage) m_storage->setEquivTrans(pos, equivTrans);
}
bool Catalog::setModified(DocPos entry, bool modified)
{
if (modified)
{
if (d._modifiedEntries.contains(entry))
return false;
d._modifiedEntries.insert(entry);
}
else
d._modifiedEntries.remove(entry);
return true;
}
bool Catalog::isModified(DocPos entry) const
{
return d._modifiedEntries.contains(entry);
}
bool Catalog::isModified(int entry) const
{
if (!isPlural(entry))
return isModified(DocPos(entry,0));
int f=numberOfPluralForms();
while(--f>=0)
if (isModified(DocPos(entry,f)))
return true;
return false;
}
//END UNDO/REDO
int findNextInList(const QLinkedList& list, int index)
{
int nextIndex=-1;
foreach(int key, list)
{
if (Q_UNLIKELY( key>index ))
{
nextIndex = key;
break;
}
}
return nextIndex;
}
int findPrevInList(const QLinkedList& list, int index)
{
int prevIndex=-1;
foreach(int key, list)
{
if (Q_UNLIKELY( key>=index ))
break;
prevIndex = key;
}
return prevIndex;
}
void insertInList(QLinkedList& list, int index)
{
QLinkedList::Iterator it=list.begin();
while(it != list.end() && index > *it)
++it;
list.insert(it,index);
}
void Catalog::setBookmark(uint idx, bool set)
{
if (set)
insertInList(d._bookmarkIndex,idx);
else
d._bookmarkIndex.removeAll(idx);
}
bool isApproved(TargetState state, ProjectLocal::PersonRole role)
{
static const TargetState marginStates[]={Translated, Final, SignedOff};
return state>=marginStates[role];
}
bool isApproved(TargetState state)
{
static const TargetState marginStates[]={Translated, Final, SignedOff};
return state==marginStates[0] || state==marginStates[1] || state==marginStates[2];
}
TargetState closestState(bool approved, ProjectLocal::PersonRole role)
{
Q_ASSERT(role!=ProjectLocal::Undefined);
static const TargetState approvementStates[][3]={
{NeedsTranslation, NeedsReviewTranslation, NeedsReviewTranslation},
{Translated, Final, SignedOff}
};
return approvementStates[approved][role];
}
bool Catalog::isObsolete(int entry) const
{
if (Q_UNLIKELY( !m_storage ))
return false;
return m_storage->isObsolete(entry);
}
QString Catalog::originalOdfFilePath()
{
if (Q_UNLIKELY( !m_storage ))
return QString();
return m_storage->originalOdfFilePath();
}
void Catalog::setOriginalOdfFilePath(const QString& odfFilePath)
{
if (Q_UNLIKELY( !m_storage ))
return;
m_storage->setOriginalOdfFilePath(odfFilePath);
}
diff --git a/src/catalog/gettext/catalogfileplugin.h b/src/catalog/gettext/catalogfileplugin.h
index e0ac987..e26111b 100644
--- a/src/catalog/gettext/catalogfileplugin.h
+++ b/src/catalog/gettext/catalogfileplugin.h
@@ -1,150 +1,149 @@
/* ****************************************************************************
This file is part of KAider
This file contains parts of KBabel code
Copyright (C) 2002-2003 by Stanislav Visnovsky
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 KATALOGFILEPLUGIN_H
#define KATALOGFILEPLUGIN_H
-#include
#include
#include
class QIODevice;
class QString;
class QStringList;
namespace GettextCatalog {
class GettextStorage;
class CatalogItem;
class CatalogImportPluginPrivate;
class CatalogExportPluginPrivate;
/**
* Result of the conversion
*/
enum ConversionStatus {
OK=0,
NOT_IMPLEMENTED,
NO_FILE,
NO_PERMISSIONS,
PARSE_ERROR,
RECOVERED_PARSE_ERROR,
OS_ERROR,
NO_PLUGIN,
UNSUPPORTED_TYPE,
RECOVERED_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)
};
/**
* HISTORY: this was a base class for Catalog import plugins in KBabel,
* but this architecture isn't not suitable for XML-based files
* (when whole DOM-tree is stored in memory to prevent file clashes)
*
* 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 GettextCatalog import plugins
* @author Stanislav Visnovsky
*/
class CatalogImportPlugin
{
public:
CatalogImportPlugin();
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(QIODevice*, GettextStorage* catalog, int* errorLine);
/**
* 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(QIODevice*) = 0;
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 QList& errors);
/** 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 character encoding used in the catalog file. */
void setCodec( QTextCodec* codec );
/** 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();
short _maxLineLength;
short _trailingNewLines;
int _errorLine;
private:
CatalogImportPluginPrivate* d;
};
}
#endif
diff --git a/src/catalog/gettext/catalogitem.cpp b/src/catalog/gettext/catalogitem.cpp
index 9c30258..5e32d8b 100644
--- a/src/catalog/gettext/catalogitem.cpp
+++ b/src/catalog/gettext/catalogitem.cpp
@@ -1,353 +1,352 @@
/* ****************************************************************************
This file is based on the one from KBabel
Copyright (C) 1999-2000 by Matthias Kiefer
2002 by Stanislav Visnovsky
Copyright (C) 2006 by Nicolas GOUTTE
2007-2012 by Nick Shaforostoff
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 "lokalize_debug.h"
-#include
#include
using namespace GettextCatalog;
QString CatalogItem::msgctxt(const bool noNewlines) const
{
QString msgctxt=d._msgctxt;
if (noNewlines) return msgctxt.replace(QLatin1Char('\n'), QLatin1Char(' ')); //" " or "" ?
else return msgctxt;
}
const QString& CatalogItem::msgstr(const int form) const
{
- if (KDE_ISLIKELY (form& CatalogItem::msgstrPlural() const
{
return d._msgstrPlural;
}
QStringList CatalogItem::allPluralForms(CatalogItem::Part part, bool stripNewLines) const
{
QStringList result=(part==CatalogItem::Source?d._msgidPlural:d._msgstrPlural).toList();
if (stripNewLines)
{
result.replaceInStrings(QStringLiteral("\n"), QString());
}
return result;
}
void CatalogItem::setMsgctxt(const QString& msg)
{
d._msgctxt=msg;
d._msgctxt.squeeze();
d._keepEmptyMsgCtxt=msg.isEmpty();
}
void CatalogItem::setMsgid(const QString& msg, const int form)
{
if (form>=d._msgidPlural.size())
d._msgidPlural.resize(form+1);
d._msgidPlural[form]=msg;
}
void CatalogItem::setMsgid(const QStringList& msg)
{
d._msgidPlural=msg.toVector(); //TODO
for (QVector::iterator it=d._msgidPlural.begin();it!=d._msgidPlural.end();++it)
it->squeeze();
}
void CatalogItem::setMsgid(const QStringList& msg, bool prependEmptyLine)
{
d._prependMsgIdEmptyLine=prependEmptyLine;
d._msgidPlural=msg.toVector(); //TODO
for (QVector::iterator it=d._msgidPlural.begin();it!=d._msgidPlural.end();++it)
it->squeeze();
}
void CatalogItem::setMsgid(const QVector& msg)
{
d._msgidPlural=msg;
for (QVector::iterator it=d._msgidPlural.begin();it!=d._msgidPlural.end();++it)
it->squeeze();
}
void CatalogItem::setMsgstr(const QString& msg, const int form)
{
if (form>=d._msgstrPlural.size())
d._msgstrPlural.resize(form+1);
d._msgstrPlural[form]=msg;
}
void CatalogItem::setMsgstr(const QStringList& msg)
{
//TODO
d._msgstrPlural=msg.toVector();
}
void CatalogItem::setMsgstr(const QStringList& msg, bool prependEmptyLine)
{
d._prependMsgStrEmptyLine=prependEmptyLine;
d._msgstrPlural=msg.toVector();
}
void CatalogItem::setMsgstr(const QVector& msg)
{
d._msgstrPlural=msg;
}
void CatalogItem::setComment(const QString& com)
{
{
//static QMutex reMutex;
//QMutexLocker reLock(&reMutex); //avoid crash #281033
//now we have a bigger scale mutex in GettextStorage
static QRegExp fuzzyRegExp(QStringLiteral("((?:^|\n)#(?:,[^,]*)*),\\s*fuzzy"));
d._fuzzyCached=com.contains( fuzzyRegExp );
}
d._comment=com;
d._comment.squeeze();
}
bool CatalogItem::isUntranslated() const
{
return d.isUntranslated();
}
bool CatalogItem::isUntranslated(uint form) const
{
return d.isUntranslated(form);
}
#if 0
QStringList CatalogItem::errors() const
{
return d._errors;
}
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.indexOf( QRegExp(",\\s*(?:possible-)c-format") ) == -1;
}
bool CatalogItem::isNoCformat() const
{
return d._comment.indexOf( QRegExp(",\\s*no-c-format") ) == -1;
}
bool CatalogItem::isQtformat() const
{
return d._comment.indexOf( QRegExp(",\\s*qt-format") ) == -1;
}
bool CatalogItem::isNoQtformat() const
{
return d._comment.indexOf( QRegExp(",\\s*no-qt-format") ) == -1;
}
bool CatalogItem::isUntranslated() const
{
return d._msgstr.first().isEmpty();
}
int CatalogItem::totalLines() const
{
int lines=0;
if(!d._comment.isEmpty())
{
lines = d._comment.count('\n')+1;
}
int msgctxtLines=0;
if(!d._msgctxt.isEmpty())
{
msgctxtLines=d._msgctxt.count('\n')+1;
}
int msgidLines=0;
QStringList::ConstIterator it;
for(it=d._msgid.begin(); it != d._msgid.end(); ++it)
{
msgidLines += (*it).count('\n')+1;
}
int msgstrLines=0;
for(it=d._msgstr.begin(); it != d._msgstr.end(); ++it)
{
msgstrLines += (*it).count('\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.removeAll("syntax error");
}
#endif
QStringList CatalogItem::msgstrAsList() const
{
if (d._msgstrPlural.isEmpty())
{
qCWarning(LOKALIZE_LOG)<<"This should never happen!";
return QStringList();
}
QStringList list(d._msgstrPlural.first().split('\n', QString::SkipEmptyParts ));
if(d._msgstrPlural.first()==QLatin1String("\n"))
list.prepend(QString());
if(list.isEmpty())
list.append(QString());
return list;
}
void CatalogItem::setFuzzy()
{
d._fuzzyCached=true;
if (d._comment.isEmpty())
{
d._comment=QStringLiteral("#, fuzzy");
return;
}
int p=d._comment.indexOf(QLatin1String("#,"));
if(p!=-1)
{
d._comment.replace(p,2,QStringLiteral("#, fuzzy,"));
return;
}
QString comment=d._comment;
static QRegExp a("\\#\\:[^\n]*\n");
p=a.indexIn(comment);
if (p!=-1)
{
d._comment=comment.insert(p+a.matchedLength(),QLatin1String("#, fuzzy\n"));
return;
}
p=d._comment.indexOf(QLatin1String("\n#|"));
if (p!=-1)
{
d._comment.insert(p,QLatin1String("\n#, fuzzy"));
return;
}
if (d._comment.startsWith(QLatin1String("#|")))
{
d._comment.prepend(QLatin1String("#, fuzzy\n"));
return;
}
if( !(d._comment.endsWith(QLatin1Char('\n'))) )
d._comment+=QLatin1Char('\n');
d._comment+=QLatin1String("#, fuzzy");
}
void CatalogItem::unsetFuzzy()
{
d._fuzzyCached=false;
static const QRegExp rmFuzzyRe(QStringLiteral(",\\s*fuzzy"));
d._comment.remove( rmFuzzyRe );
// remove empty comment lines
d._comment.remove( QRegExp(QStringLiteral("\n#\\s*$")) );
d._comment.remove( QRegExp(QStringLiteral("^#\\s*$")) );
d._comment.remove( QRegExp(QStringLiteral("#\\s*\n")) );
d._comment.remove( QRegExp(QStringLiteral("^#\\s*\n")) );
}
#if 0
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.removeAt( d._errors.indexOf( error ) );
}
#endif
// kate: space-indent on; indent-width 4; replace-tabs on;
diff --git a/src/catalog/gettextheader.cpp b/src/catalog/gettextheader.cpp
index d958ecd..fdcb4ad 100644
--- a/src/catalog/gettextheader.cpp
+++ b/src/catalog/gettextheader.cpp
@@ -1,746 +1,745 @@
/* ****************************************************************************
This file is part of Lokalize
Copyright (C) 2008-2014 by Nick Shaforostoff
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) version 3 or any later version
accepted by the membership of KDE e.V. (or its successor approved
by the membership of KDE e.V.), which shall act as a proxy
defined in Section 14 of version 3 of the license.
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, see .
**************************************************************************** */
#include "gettextheader.h"
#include "lokalize_debug.h"
#include "project.h"
#include "version.h"
#include "prefs_lokalize.h"
#include "prefs.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
-#include
#include
/**
* this data was obtained by running GNUPluralForms()
* on all languages KDE knows of
**/
struct langPInfo
{
const char *lang;
const char *plural;
};
static const langPInfo langsWithPInfo[] = {
{ "ar", "nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;" },
{ "be@latin", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" },
{ "be", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" },
{ "br", "nplurals=1; plural=0;" },
{ "bs", "nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;" },
{ "csb", "nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)" },
{ "cs", "nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;" },
{ "da", "nplurals=2; plural=(n != 1);" },
{ "de", "nplurals=2; plural=(n != 1);" },
{ "el", "nplurals=2; plural=(n != 1);" },
{ "en", "nplurals=2; plural=(n != 1);" },
{ "en_GB", "nplurals=2; plural=(n != 1);" },
{ "en_US", "nplurals=2; plural=(n != 1);" },
{ "eo", "nplurals=2; plural=(n != 1);" },
{ "es", "nplurals=2; plural=(n != 1);" },
{ "et", "nplurals=2; plural=(n != 1);" },
{ "fa", "nplurals=1; plural=0;" },
{ "fi", "nplurals=2; plural=(n != 1);" },
{ "fo", "nplurals=2; plural=(n != 1);" },
{ "fr", "nplurals=2; plural=(n > 1);" },
{ "ga", "nplurals=5; plural=n==1 ? 0 : n==2 ? 1 : n<7 ? 2 : n < 11 ? 3 : 4" },
{ "gd", "nplurals=4; plural=(n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : (n > 2 && n < 20) ? 2 : 3;" },
{ "gu", "nplurals=2; plural=(n!=1);" },
{ "he", "nplurals=2; plural=(n != 1);" },
{ "hi", "nplurals=2; plural=(n!=1);" },
{ "hne", "nplurals=2; plural=(n!=1);" },
{ "hr", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" },
{ "hsb", "nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3;" },
{ "hu", "nplurals=2; plural=(n != 1);" },
{ "hy", "nplurals=4; plural=n==1 ? 3 : n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;" },
{ "id", "nplurals=1; plural=0;" },
{ "it", "nplurals=2; plural=(n != 1);" },
{ "ja", "nplurals=1; plural=0;" },
{ "ka", "nplurals=1; plural=0;" },
{ "kk", "nplurals=1; plural=0;" },
{ "km", "nplurals=1; plural=0;" },
{ "ko", "nplurals=1; plural=0;" },
{ "lt", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2);" },
{ "lv", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2);" },
{ "mai", "nplurals=2; plural=(n!=1);" },
{ "mk", "nplurals=3; plural=n%10==1 ? 0 : n%10==2 ? 1 : 2;" },
{ "mr", "nplurals=2; plural=(n!=1);" },
{ "ms", "nplurals=2; plural=1;" },
{ "nb", "nplurals=2; plural=(n != 1);" },
{ "nl", "nplurals=2; plural=(n != 1);" },
{ "nn", "nplurals=2; plural=(n != 1);" },
{ "oc", "nplurals=2; plural=(n > 1);" },
{ "or", "nplurals=2; plural=(n!=1);" },
{ "pl", "nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" },
{ "pt", "nplurals=2; plural=(n != 1);" },
{ "pt_BR", "nplurals=2; plural=(n > 1);" },
{ "ro", "nplurals=3; plural=n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2;" },
{ "ru", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" },
{ "sk", "nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;" },
{ "sl", "nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || n%100==4 ? 3 : 0);" },
{ "sr", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" },
{ "sr@latin", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" },
{ "sv", "nplurals=2; plural=(n != 1);" },
{ "te", "nplurals=5; plural=n==1 ? 0 : n==2 ? 1 : n<7 ? 2 : n<11 ? 3 : 4;" },
{ "th", "nplurals=1; plural=0;" },
{ "tr", "nplurals=2; plural=(n > 1);" },
{ "ug", "nplurals=1; plural=0;" },
{ "uk", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" },
{ "uz", "nplurals=1; plural=0;" },
{ "uz@cyrillic", "nplurals=1; plural=0;" },
{ "vi", "nplurals=1; plural=0;" },
{ "zh_CN", "nplurals=1; plural=0;" },
{ "zh_HK", "nplurals=1; plural=0;" },
{ "zh_TW", "nplurals=1; plural=0;" }
};
static const size_t langsWithPInfoCount = sizeof (langsWithPInfo) / sizeof (langsWithPInfo[0]);
int numberOfPluralFormsFromHeader(const QString& header)
{
QRegExp rxplural(QStringLiteral("Plural-Forms:\\s*nplurals=(.);"));
if (rxplural.indexIn(header) == -1)
return 0;
bool ok;
int result=rxplural.cap(1).toShort(&ok);
return ok?result:0;
}
int numberOfPluralFormsForLangCode(const QString& langCode)
{
QString expr=GNUPluralForms(langCode);
QRegExp rxplural(QStringLiteral("nplurals=(.);"));
if (rxplural.indexIn(expr) == -1)
return 0;
bool ok;
int result=rxplural.cap(1).toShort(&ok);
return ok?result:0;
}
QString GNUPluralForms(const QString& lang)
{
QByteArray l(lang.toUtf8());
int i=langsWithPInfoCount;
while(--i>=0 && l!=langsWithPInfo[i].lang)
;
- if (KDE_ISLIKELY( i>=0 ))
+ if (Q_LIKELY( i>=0 ))
return QString::fromLatin1(langsWithPInfo[i].plural);
i=langsWithPInfoCount;
while(--i>=0 && !l.startsWith(langsWithPInfo[i].lang))
;
- if (KDE_ISLIKELY( i>=0 ))
+ if (Q_LIKELY( i>=0 ))
return QString::fromLatin1(langsWithPInfo[i].plural);
//BEGIN alternative
// NOTE does this work under M$ OS?
qCWarning(LOKALIZE_LOG)<<"gonna call msginit";
QString def=QStringLiteral("nplurals=2; plural=n != 1;");
QStringList arguments;
arguments << QLatin1String("-l") << lang
<< QLatin1String("-i") << QLatin1String("-")
<< QLatin1String("-o") << QLatin1String("-")
<< QLatin1String("--no-translator")
<< QLatin1String("--no-wrap");
QProcess msginit;
msginit.start(QLatin1String("msginit"), arguments);
msginit.waitForStarted(5000);
if (Q_UNLIKELY( msginit.state()!=QProcess::Running ))
{
//qCWarning(LOKALIZE_LOG)<<"msginit error";
return def;
}
msginit.write(
"# SOME DESCRIPTIVE TITLE.\n"
"# Copyright (C) YEAR Free Software Foundation, Inc.\n"
"# FIRST AUTHOR , YEAR.\n"
"#\n"
"#, fuzzy\n"
"msgid \"\"\n"
"msgstr \"\"\n"
"\"Project-Id-Version: PACKAGE VERSION\\n\"\n"
"\"POT-Creation-Date: 2002-06-25 03:23+0200\\n\"\n"
"\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n"
"\"Last-Translator: FULL NAME \\n\"\n"
"\"Language-Team: LANGUAGE \\n\"\n"
"\"Language: LL\\n\"\n"
"\"MIME-Version: 1.0\\n\"\n"
"\"Content-Type: text/plain; charset=UTF-8\\n\"\n"
"\"Content-Transfer-Encoding: ENCODING\\n\"\n"
// "\"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\\n\"\n"
);
msginit.closeWriteChannel();
if (Q_UNLIKELY( !msginit.waitForFinished(5000) ))
{
qCWarning(LOKALIZE_LOG)<<"msginit error";
return def;
}
QByteArray result = msginit.readAll();
int pos = result.indexOf("Plural-Forms: ");
if (Q_UNLIKELY( pos==-1 ))
{
//qCWarning(LOKALIZE_LOG)<<"msginit error"<');
temp=QStringLiteral("Last-Translator: ") % authorNameEmail % BACKSLASH_N;
QRegExp lt(QStringLiteral("^ *Last-Translator:.*"));
for ( it = headerList.begin(),found=false; it != headerList.end() && !found; ++it )
{
if (it->contains(lt))
{
if (forSaving) *it = temp;
found=true;
}
}
if (Q_UNLIKELY( !found ))
headerList.append(temp);
QLocale cLocale(QLocale::C);
QString dateTimeString = cLocale.toString(QDateTime::currentDateTime(), QStringLiteral("yyyy-MM-dd hh:mm"));
QString zoneOffsetString1 = QTimeZone(QTimeZone::systemTimeZoneId()).displayName(QTimeZone::GenericTime, QTimeZone::OffsetName);
int zpos=qMax(qMax(0, zoneOffsetString1.indexOf('+')), zoneOffsetString1.indexOf('-'));
QString zoneOffsetString = QString::fromRawData(zoneOffsetString1.unicode()+zpos, zoneOffsetString1.length()-zpos);
temp=QStringLiteral("PO-Revision-Date: ") % dateTimeString % zoneOffsetString.remove(':') % BACKSLASH_N;
QRegExp poRevDate(QStringLiteral("^ *PO-Revision-Date:.*"));
for ( it = headerList.begin(),found=false; it != headerList.end() && !found; ++it )
{
found=it->contains(poRevDate);
if (found && forSaving) *it = temp;
}
if (Q_UNLIKELY( !found ))
headerList.append(temp);
temp=QStringLiteral("Project-Id-Version: ") % CatalogProjectId % BACKSLASH_N;
//temp.replace( "@PACKAGE@", packageName());
QRegExp projectIdVer(QStringLiteral("^ *Project-Id-Version:.*"));
for ( it = headerList.begin(),found=false; it != headerList.end() && !found; ++it )
{
found=it->contains(projectIdVer);
if (found && it->contains(QLatin1String("PACKAGE VERSION")))
*it = temp;
}
if (Q_UNLIKELY( !found ))
headerList.append(temp);
langCode=Project::instance()->isLoaded()?
Project::instance()->langCode():
Settings::defaultLangCode();
QString language; //initialized with preexisting value or later
QString mailingList; //initialized with preexisting value or later
static QMap langEnums;
if (!langEnums.size())
for (int l=QLocale::Abkhazian; l<=QLocale::Akoose; ++l)
langEnums[QLocale::languageToString((QLocale::Language)l)]=(QLocale::Language)l;
static QRegExp langTeamRegExp(QStringLiteral("^ *Language-Team:.*"));
for ( it = headerList.begin(),found=false; it != headerList.end() && !found; ++it )
{
found=it->contains(langTeamRegExp);
if (found)
{
//really parse header
QRegExp re(QStringLiteral("^ *Language-Team: *(.*) *<([^>]*)>"));
if (re.indexIn(*it) != -1 )
{
if (langEnums.contains( re.cap(1).trimmed() ))
{
language=re.cap(1).trimmed();
mailingList=re.cap(2).trimmed();
QList locales = QLocale::matchingLocales(langEnums.value(language), QLocale::AnyScript, QLocale::AnyCountry);
if (locales.size()) langCode=locales.first().name().left(2);
}
}
ait=it;
}
}
if (language.isEmpty())
{
language=QLocale::languageToString(QLocale(langCode).language());
if (language.isEmpty())
language=langCode;
}
if (mailingList.isEmpty() || belongsToProject)
{
if (Project::instance()->isLoaded())
mailingList=Project::instance()->mailingList();
else //if (mailingList.isEmpty())
mailingList=Settings::defaultMailingList();
}
temp=QStringLiteral("Language-Team: ")%language%QStringLiteral(" <")%mailingList%QStringLiteral(">\\n");
- if (KDE_ISLIKELY( found ))
+ if (Q_LIKELY( found ))
(*ait) = temp;
else
headerList.append(temp);
static QRegExp langCodeRegExp(QStringLiteral("^ *Language: *([^ \\\\]*)"));
temp=QStringLiteral("Language: ") % langCode % BACKSLASH_N;
for ( it = headerList.begin(),found=false; it != headerList.end() && !found; ++it )
{
found=(langCodeRegExp.indexIn(*it)!=-1);
if (found && langCodeRegExp.cap(1).isEmpty())
*it=temp;
//if (found) qCWarning(LOKALIZE_LOG)<<"got explicit lang code:"<contains(ctRe);
if (found) *it=temp;
}
if (Q_UNLIKELY( !found ))
headerList.append(temp);
temp=QStringLiteral("Content-Transfer-Encoding: 8bit\\n");
QRegExp cteRe(QStringLiteral("^ *Content-Transfer-Encoding:.*"));
for ( it = headerList.begin(),found=false; it != headerList.end() && !found; ++it )
found=it->contains(cteRe);
if (!found)
headerList.append(temp);
// ensure MIME-Version header
temp=QStringLiteral("MIME-Version: 1.0\\n");
QRegExp mvRe(QStringLiteral("^ *MIME-Version:"));
for ( it = headerList.begin(),found=false; it != headerList.end()&& !found; ++it )
{
found=it->contains(mvRe);
if (found) *it = temp;
}
if (Q_UNLIKELY( !found ))
headerList.append(temp);
//qCDebug(LOKALIZE_LOG)<<"testing for GNUPluralForms";
// update plural form header
QRegExp pfRe(QStringLiteral("^ *Plural-Forms:"));
for ( it = headerList.begin(),found=false; it != headerList.end()&& !found; ++it )
found=it->contains(pfRe);
if (found)
{
--it;
//qCDebug(LOKALIZE_LOG)<<"GNUPluralForms found";
int num=numberOfPluralFormsFromHeader(header);
if (!num)
{
if (generatedFromDocbook)
num=1;
else
{
qCWarning(LOKALIZE_LOG)<<"No plural form info in header, using project-defined one"<replace(pf,temp);
num=numberOfPluralFormsFromHeader(temp);
}
else
{
qCWarning(LOKALIZE_LOG)<<"no... smth went wrong :(\ncheck your gettext install";
num=2;
}
}
}
numberOfPluralForms=num;
}
else if ( !generatedFromDocbook)
{
//qCDebug(LOKALIZE_LOG)<<"generating GNUPluralForms"<contains(xgRe);
if (found) *it = temp;
}
if (Q_UNLIKELY( !found ))
headerList.append(temp);
//m_header.setMsgstr( headerList.join( "\n" ) );
header=headerList.join(QStringLiteral("\n"));
//END header itself
//BEGIN comment = description, copyrights
// U+00A9 is the Copyright sign
QRegExp fsfc(QStringLiteral("^# *Copyright (\\(C\\)|\\x00a9).*Free Software Foundation, Inc"));
for ( it = commentList.begin(),found=false; it != commentList.end()&&!found; ++it )
{
found=it->contains( fsfc ) ;
if (found)
it->replace(QStringLiteral("YEAR"), cLocale.toString(QDate::currentDate(), QStringLiteral("yyyy")));
}
/*
if( saveOptions.FSFCopyright == ProjectSettingsBase::Update )
{
//update years
QString cy = cLocale.toString(QDate::currentDate(), "yyyy");
if( !it->contains( QRegExp(cy)) ) // is the year already included?
{
int index = it->lastIndexOf( 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 0
if ( ( !usePrefs || saveOptions.updateDescription )
&& ( !saveOptions.descriptionString.isEmpty() ) )
{
temp = "# "+saveOptions.descriptionString;
temp.replace( "@PACKAGE@", packageName());
temp.replace( "@LANGUAGE@", identityOptions.languageName);
temp = temp.trimmed();
// The description strings has often buggy variants already in the file, these must be removed
QString regexpstr = "^#\\s+" + QRegExp::escape( saveOptions.descriptionString.trimmed() ) + "\\s*$";
regexpstr.replace( "@PACKAGE@", ".*" );
regexpstr.replace( "@LANGUAGE@", ".*" );
//qCDebug(LOKALIZE_LOG) << "REGEXPSTR: " << regexpstr;
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 .* to .*\\s*$" );
qCDebug(LOKALIZE_LOG) << "Temp is '" << temp << "'";
found=false;
bool foundTemplate=false;
it = commentList.begin();
while ( it != commentList.end() )
{
qCDebug(LOKALIZE_LOG) << "testing '" << (*it) << "'";
bool deleteItem = false;
if ( (*it) == temp )
{
qCDebug(LOKALIZE_LOG) << "Match ";
if ( found )
deleteItem = true;
else
found=true;
}
else if ( regexp.indexIn( *it ) >= 0 )
{
// We have a similar (translated) string (from another project or another language (perhaps typos)). Remove it.
deleteItem = true;
}
else if ( regexpUntranslated.indexIn( *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.erase( it );
else
++it;
}
if (!found) commentList.prepend(temp);
}
#endif
// qCDebug(LOKALIZE_LOG) << "HEADER COMMENT: " << commentList;
/* if ( (!usePrefs || saveOptions.updateTranslatorCopyright)
&& ( ! identityOptions->readEntry("authorName","").isEmpty() )
&& ( ! identityOptions->readEntry("Email","").isEmpty() ) ) // An email address can be used as ersatz of a name
{*/
// return;
QStringList foundAuthors;
temp=QStringLiteral("# ")%authorNameEmail%QStringLiteral(", ")%cLocale.toString(QDate::currentDate(), QStringLiteral("yyyy"))%'.';
// ### TODO: it would be nice if the entry could start with "COPYRIGHT" and have the "(C)" symbol (both not mandatory)
QRegExp regexpAuthorYear( QStringLiteral("^#.*(<.+@.+>)?,\\s*([\\d]+[\\d\\-, ]*|YEAR)") );
QRegExp regexpYearAlone( QStringLiteral("^# , \\d{4}.?\\s*$") );
if (commentList.isEmpty())
{
commentList.append(temp);
commentList.append(QString());
}
else
{
it = commentList.begin();
while ( it != commentList.end() )
{
bool deleteItem = false;
if ( it->indexOf( QLatin1String("copyright"), 0, Qt::CaseInsensitive ) != -1 )
{
// We have a line with a copyright. It should not be moved.
}
else if ( it->contains( QRegExp(QStringLiteral("#, *fuzzy")) ) )
deleteItem = true;
else if ( it->contains( regexpYearAlone ) )
{
// We have found a year number that is preceded 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->contains( QLatin1String("# FIRST AUTHOR , YEAR.")) )
deleteItem = true;
else if ( it->contains( QLatin1String("# SOME DESCRIPTIVE TITLE")))
deleteItem = true;
else if ( it->contains( regexpAuthorYear ) ) // email address followed by year
{
if ( !foundAuthors.contains( (*it) ) )
{
// 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.erase( it );
else
++it;
}
if ( !foundAuthors.isEmpty() )
{
found = false;
bool foundAuthor = false;
const QString cy = cLocale.toString(QDate::currentDate(), QStringLiteral("yyyy"));
ait = foundAuthors.end();
for ( it = foundAuthors.begin() ; it!=foundAuthors.end(); ++it )
{
if ( it->contains(Settings::authorName()) || it->contains(Settings::authorEmail()) )
{
foundAuthor = true;
if ( it->contains( cy ) )
found = true;
else
ait = it;
}
}
if ( !found )
{
if ( !foundAuthor )
foundAuthors.append(temp);
else if ( ait != foundAuthors.end() )
{
//update years
const int index = (*ait).lastIndexOf( QRegExp(QStringLiteral("[\\d]+[\\d\\-, ]*")) );
if ( index == -1 )
(*ait)+=QStringLiteral(", ")%cy;
else
ait->insert(index+1, QStringLiteral(", ")%cy);
}
else
qCDebug(LOKALIZE_LOG) << "INTERNAL ERROR: author found but iterator dangling!";
}
}
else
foundAuthors.append(temp);
foreach (QString author, foundAuthors)
{
// ensure dot at the end of copyright
if ( !author.endsWith(QLatin1Char('.')) ) author += QLatin1Char('.');
commentList.append(author);
}
}
//m_header.setComment( commentList.join( "\n" ) );
comment=commentList.join(QStringLiteral("\n"));
//END comment = description, copyrights
}
QString fullUserName();// defined in helpers.cpp
bool askAuthorInfoIfEmpty()
{
if(QThread::currentThread() == qApp->thread())
{
if (Settings::authorName().isEmpty())
{
bool ok;
QString contact = QInputDialog::getText(
SettingsController::instance()->mainWindowPtr(),
i18nc("@window:title", "Author name missing"), i18n("Your name:"),
QLineEdit::Normal, fullUserName(), &ok);
#ifndef NOKDE
Settings::self()->authorNameItem()->setValue(ok?contact:fullUserName());
Settings::self()->save();
#else
Settings::self()->setAuthorName(ok?contact:fullUserName());
#endif
}
if (Settings::authorEmail().isEmpty())
{
bool ok;
QString email = QInputDialog::getText(
SettingsController::instance()->mainWindowPtr(),
i18nc("@window:title", "Author email missing"), i18n("Your email:"),
QLineEdit::Normal, QString(), &ok);
if (ok)
{
#ifndef NOKDE
Settings::self()->authorEmailItem()->setValue(email);
Settings::self()->save();
#else
Settings::self()->setAuthorEmail(email);
#endif
}
}
}
return !Settings::authorName().isEmpty() && !Settings::authorEmail().isEmpty();
}
diff --git a/src/catalog/phase.cpp b/src/catalog/phase.cpp
index ed0a6cb..6f5decf 100644
--- a/src/catalog/phase.cpp
+++ b/src/catalog/phase.cpp
@@ -1,100 +1,98 @@
/* ****************************************************************************
This file is part of Lokalize
Copyright (C) 2009 by Nick Shaforostoff
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) version 3 or any later version
accepted by the membership of KDE e.V. (or its successor approved
by the membership of KDE e.V.), which shall act as a proxy
defined in Section 14 of version 3 of the license.
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, see .
**************************************************************************** */
#include "phase.h"
#include "cmd.h"
#include "catalog.h"
#include "project.h"
#include "prefs_lokalize.h"
#include "gettextheader.h"
#include
-#include "kdemacros.h"
-
#include
const char* const* processes()
{
static const char* const processes[]={"translation","review","approval"};
return processes;
}
//guess role
ProjectLocal::PersonRole roleForProcess(const QString& process)
{
int i=ProjectLocal::Undefined;
while (i>=0 && !process.startsWith(processes()[--i]))
;
return (i==-1)?Project::local()->role():ProjectLocal::PersonRole(i);
}
void generatePhaseForCatalogIfNeeded(Catalog* catalog)
{
- if (KDE_ISLIKELY( !(catalog->capabilities()&Phases) || catalog->activePhaseRole()==ProjectLocal::Undefined ))
+ if (Q_LIKELY( !(catalog->capabilities()&Phases) || catalog->activePhaseRole()==ProjectLocal::Undefined ))
return;
Phase phase;
phase.process=processes()[Project::local()->role()];
if (initPhaseForCatalog(catalog, phase))
static_cast(catalog)->push(new UpdatePhaseCmd(catalog, phase));
catalog->setActivePhase(phase.name, roleForProcess(phase.process));
}
bool initPhaseForCatalog(Catalog* catalog, Phase& phase, int options)
{
askAuthorInfoIfEmpty();
phase.contact=Settings::authorName();
QSet names;
QList phases=catalog->allPhases();
qSort(phases.begin(), phases.end(), qGreater());
foreach (const Phase& p, phases)
{
if (!(options&ForceAdd) && p.contact==phase.contact && p.process==phase.process)
{
phase=p;
break;
}
names.insert(p.name);
}
if (phase.name.isEmpty())
{
int i=0;
while (names.contains(phase.name=phase.process+QStringLiteral("-%1").arg(++i)))
;
phase.date=QDate::currentDate();
phase.email=Settings::authorEmail();
return true;
}
return false;
}
Phase::Phase()
: date(QDate::currentDate())
, tool(QStringLiteral("lokalize-" LOKALIZE_VERSION))
{}
diff --git a/src/catalog/pos.cpp b/src/catalog/pos.cpp
index 7deadc6..8f77500 100644
--- a/src/catalog/pos.cpp
+++ b/src/catalog/pos.cpp
@@ -1,166 +1,165 @@
/* ****************************************************************************
This file is part of Lokalize
Copyright (C) 2007 by Nick Shaforostoff
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 "pos.h"
#include "catalog.h"
-#include "kdemacros.h"
bool switchPrev(Catalog*& catalog,DocPosition& pos,int parts)
{
bool switchEntry=false;
bool switchCommentIndex=false;
if (pos.part==DocPosition::Comment)
switchCommentIndex=true;
else if (pos.part==DocPosition::Target)
{
if (parts&DocPosition::Source)
pos.part=DocPosition::Source;
switchEntry=!(parts&DocPosition::Source);
}
else if (pos.part==DocPosition::Source)
switchEntry=true;
bool skipCommentThisTime=false;
if (switchCommentIndex)
{
if (pos.form)
pos.form--;
switchEntry=pos.form; //pos.form is zero again
skipCommentThisTime=pos.form;
}
if (!switchEntry)
return true;
- if (KDE_ISUNLIKELY( pos.form>0
+ if (Q_UNLIKELY( pos.form>0
&& catalog->isPlural(pos.entry)))
pos.form--;
- else if (KDE_ISUNLIKELY( pos.entry==0 ))
+ else if (Q_UNLIKELY( pos.entry==0 ))
return false;
else
{
pos.entry--;
pos.form=catalog->isPlural(pos.entry)*(catalog->numberOfPluralForms()-1);
}
pos.offset=0;
if (parts&DocPosition::Comment && !skipCommentThisTime && pos.form==0 && catalog->notes(pos).size())
{
pos.part=DocPosition::Comment;
pos.form=catalog->notes(pos).size()-1;
}
else
pos.part=DocPosition::Target;
return true;
}
bool switchNext(Catalog*& catalog,DocPosition& pos,int parts)
{
bool switchEntry=false;
bool switchCommentIndex=false;
if (pos.part==DocPosition::Source)
pos.part=DocPosition::Target;
else if (pos.part==DocPosition::Target)
{
if (parts&DocPosition::Comment && pos.form==0 && catalog->notes(pos).size())
pos.part=DocPosition::Comment;
else
switchEntry=true;
}
else if (pos.part==DocPosition::Comment)
switchCommentIndex=true;
if (switchCommentIndex)
{
pos.form++;
if (catalog->notes(pos).size()==pos.form)
{
pos.form=0;
switchEntry=true;
}
}
if (!switchEntry)
return true;
- if (KDE_ISUNLIKELY( pos.entry!=-1
+ if (Q_UNLIKELY( pos.entry!=-1
&& pos.form+1 < catalog->numberOfPluralForms()
&& catalog->isPlural(pos.entry)))
pos.form++;
- else if (KDE_ISUNLIKELY( pos.entry==catalog->numberOfEntries()-1 ))
+ else if (Q_UNLIKELY( pos.entry==catalog->numberOfEntries()-1 ))
return false;
else
{
pos.entry++;
pos.form=0;
}
pos.offset=0;
pos.part=(parts&DocPosition::Source)?DocPosition::Source:DocPosition::Target;
return true;
}
#ifndef NOKDE
#include
const QDBusArgument &operator>>(const QDBusArgument &argument, DocPosition& pos)
{
int entry;
int form;
uint offset;
argument.beginStructure();
argument >> entry >> form >> offset;
argument.endStructure();
pos.entry=entry;
pos.form=form;
pos.offset=offset;
return argument;
}
QDBusArgument &operator<<(QDBusArgument &argument, const DocPosition &pos)
{
int entry=pos.entry;
int form=pos.form;
uint offset=pos.offset;
argument.beginStructure();
argument << entry << form << offset;
argument.endStructure();
return argument;
}
#endif
diff --git a/src/catalog/xliff/xliffstorage.cpp b/src/catalog/xliff/xliffstorage.cpp
index a8e02fa..b03d1ab 100644
--- a/src/catalog/xliff/xliffstorage.cpp
+++ b/src/catalog/xliff/xliffstorage.cpp
@@ -1,1068 +1,1068 @@
/*
Copyright 2008-2009 Nick Shaforostoff
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) version 3 or any later version
accepted by the membership of KDE e.V. (or its successor approved
by the membership of KDE e.V.), which shall act as a proxy
defined in Section 14 of version 3 of the license.
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, see .
*/
#include "xliffstorage.h"
#include "lokalize_debug.h"
#include "gettextheader.h"
#include "project.h"
#include "version.h"
#include "prefs_lokalize.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef Q_OS_WIN
#define U QLatin1String
#else
#define U QStringLiteral
#endif
static const QString noyes[]={U("no"),U("yes")};
static const QString bintargettarget[]={U("bin-target"),U("target")};
static const QString binsourcesource[]={U("bin-source"),U("source")};
static const QString NOTE=U("note");
XliffStorage::XliffStorage()
: CatalogStorage()
{
}
XliffStorage::~XliffStorage()
{
}
int XliffStorage::capabilities() const
{
return KeepsNoteAuthors|MultipleNotes|Phases|ExtendedStates|Tags;
}
//BEGIN OPEN/SAVE
int XliffStorage::load(QIODevice* device)
{
QTime chrono;chrono.start();
QXmlSimpleReader reader;
reader.setFeature(QStringLiteral("http://qt-project.org/xml/features/report-whitespace-only-CharData"),true);
reader.setFeature(QStringLiteral("http://xml.org/sax/features/namespaces"),false);
QXmlInputSource source(device);
QString errorMsg;
int errorLine;//+errorColumn;
bool success=m_doc.setContent(&source, &reader, &errorMsg, &errorLine/*,errorColumn*/);
QString FILE=QStringLiteral("file");
if (!success || m_doc.elementsByTagName(FILE).isEmpty())
{
qCWarning(LOKALIZE_LOG)<0 && (++i) msgstr(item.msgstrPlural());
// while (msgstr.count() tags;
QString stringToInsert;
int pos;
int lengthOfStringToRemove;
ActionType actionType;
///Get
ContentEditingData(ActionType type=Get)
: pos(-1)
, lengthOfStringToRemove(-1)
, actionType(type)
{}
///DeleteText
ContentEditingData(int p, int l)
: pos(p)
, lengthOfStringToRemove(l)
, actionType(DeleteText)
{}
///InsertText
ContentEditingData(int p,const QString& s)
: stringToInsert(s)
, pos(p)
, lengthOfStringToRemove(-1)
, actionType(InsertText)
{}
///InsertTag
ContentEditingData(int p,const InlineTag& range)
: pos(p)
, lengthOfStringToRemove(-1)
, actionType(InsertTag)
{
tags.append(range);
}
///DeleteTag
ContentEditingData(int p)
: pos(p)
, lengthOfStringToRemove(-1)
, actionType(DeleteTag)
{}
};
static QString doContent(QDomElement elem, int startingPos, ContentEditingData* data);
/**
* walks through XLIFF XML and performs actions depending on ContentEditingData:
* - reads content
* - deletes content, or
* - inserts content
*/
static QString content(QDomElement elem, ContentEditingData* data=0)
{
return doContent(elem, 0, data);
}
static QString doContent(QDomElement elem, int startingPos, ContentEditingData* data)
{
//actually startingPos is current pos
QString result;
if (elem.isNull()
|| (!result.isEmpty() && data && data->actionType==ContentEditingData::CheckLength))
return QString();
bool seenCharacterDataAfterElement=false;
QDomNode n = elem.firstChild();
while (!n.isNull())
{
if (n.isCharacterData())
{
seenCharacterDataAfterElement=true;
QDomCharacterData c=n.toCharacterData();
QString cData=c.data();
if (data && data->pos!=-1 &&
data->pos>=startingPos && data->pos<=startingPos+cData.size())
{
// time to do some action! ;)
int localStartPos=data->pos-startingPos;
//BEGIN DELETE TEXT
if (data->actionType==ContentEditingData::DeleteText) //(data->lengthOfStringToRemove!=-1)
{
if (localStartPos+data->lengthOfStringToRemove>cData.size())
{
//text is fragmented into several QDomCharacterData
int localDelLen=cData.size()-localStartPos;
//qCWarning(LOKALIZE_LOG)<<"text is fragmented into several QDomCharacterData. localDelLen:"<lengthOfStringToRemove=data->lengthOfStringToRemove-localDelLen;
//data->pos=startingPos;
//qCWarning(LOKALIZE_LOG)<<"\tsetup:"<pos<lengthOfStringToRemove;
}
else
{
//qCWarning(LOKALIZE_LOG)<<"simple delete"<lengthOfStringToRemove;
c.deleteData(localStartPos,data->lengthOfStringToRemove);
data->actionType=ContentEditingData::CheckLength;
return QString('a');//so it exits 100%
}
}
//END DELETE TEXT
//INSERT
else if (data->actionType==ContentEditingData::InsertText)
{
c.insertData(localStartPos,data->stringToInsert);
data->actionType=ContentEditingData::CheckLength;
return QString('a');//so it exits 100%
}
//BEGIN INSERT TAG
else if (data->actionType==ContentEditingData::InsertTag)
{
const InlineTag& tag=data->tags.first();
QString mid=cData.mid(localStartPos);
qCDebug(LOKALIZE_LOG)<<"inserting tag"<pos<(tag.start+1))
{
//qCWarning(LOKALIZE_LOG)<<"isPaired";
int len=tag.end-tag.start-1;//-image symbol
int localLen=qMin(len,mid.size());
if (localLen)//appending text
{
//qCWarning(LOKALIZE_LOG)<<"localLen. appending"<missingLen (or siblings end)
int childrenCumulativeLen=0;
QDomNode sibling=newNode.nextSibling();
while(!sibling.isNull())//&&(childrenCumulativeLenmissingLen)
{
if (tmp.isCharacterData())
{
//divide the last string
const QString& endData=tmp.toCharacterData().data();
QString last=endData.left(endData.size()-(childrenCumulativeLen-missingLen));
newNode.appendChild( elem.ownerDocument().createTextNode(last));
tmp.toCharacterData().deleteData(0,last.size());
//qCWarning(LOKALIZE_LOG)<<"end of add"<actionType=ContentEditingData::CheckLength;
return QStringLiteral("a");//we're done here
}
//END INSERT TAG
cData=c.data();
}
//else
// if (data&&data->pos!=-1/*&& n.nextSibling().isNull()*/)
// qCWarning(LOKALIZE_LOG)<<"arg!"<pos"<pos;
result += cData;
startingPos+=cData.size();
}
else if (n.isElement())
{
QDomElement el=n.toElement();
//BEGIN DELETE TAG
if (data&&data->actionType==ContentEditingData::DeleteTag
&&data->pos==startingPos)
{
//qCWarning(LOKALIZE_LOG)<<"start deleting tag";
data->tags.append(InlineTag(startingPos, -1, InlineTag::getElementType(el.tagName().toUtf8()), el.attribute("id"), el.attribute("xid")));
if (data->tags.first().isPaired())
{
//get end position
ContentEditingData subData(ContentEditingData::Get);
QString subContent=doContent(el,startingPos,&subData);
data->tags[0].end=1+startingPos+subContent.size();//tagsymbol+text
//qCWarning(LOKALIZE_LOG)<<"get end position"<actionType=ContentEditingData::CheckLength;
return QStringLiteral("a");//we're done here
}
//END DELETE TAG
if (!seenCharacterDataAfterElement) //add empty charData child so that user could add some text
elem.insertBefore( elem.ownerDocument().createTextNode(QString()),n);
seenCharacterDataAfterElement=false;
if (data)
{result += QChar(TAGRANGE_IMAGE_SYMBOL); ++startingPos;}
int oldStartingPos=startingPos;
//detect type of the tag
InlineTag::InlineElement i=InlineTag::getElementType(el.tagName().toUtf8());
//1 or 2 images to represent it?
//2 = there may be content inside
if (InlineTag::isPaired(i))
{
QString recursiveContent=doContent(el,startingPos,data);
if (!recursiveContent.isEmpty())
{result += recursiveContent; startingPos+=recursiveContent.size();}
if (data)
{result += QChar(TAGRANGE_IMAGE_SYMBOL); ++startingPos;}
}
if (data&&data->actionType==ContentEditingData::Get)
{
QString id=el.attribute(QStringLiteral("id"));
if (i==InlineTag::mrk)//TODO attr map
id=el.attribute(QStringLiteral("mtype"));
//qCWarning(LOKALIZE_LOG)<<"tagName"<tags.append(InlineTag(oldStartingPos-1,startingPos-1,i,id,el.attribute(QStringLiteral("xid"))));
}
}
n = n.nextSibling();
}
if (!seenCharacterDataAfterElement)
{
//add empty charData child so that user could add some text
elem.appendChild( elem.ownerDocument().createTextNode(QString()));
}
return result;
}
//flat-model interface (ignores XLIFF grouping)
CatalogString XliffStorage::catalogString(QDomElement unit, DocPosition::Part part) const
{
static const QString names[]={U("source"),U("target"), U("seg-source")};
CatalogString catalogString;
ContentEditingData data(ContentEditingData::Get);
int nameIndex=part==DocPosition::Target;
if (nameIndex==0 && !unit.firstChildElement(names[2]).isNull()) nameIndex=2;
catalogString.string=content(unit.firstChildElement( names[nameIndex]), &data );
catalogString.tags=data.tags;
return catalogString;
}
CatalogString XliffStorage::catalogString(const DocPosition& pos) const
{
return catalogString(unitForPos(pos.entry), pos.part);
}
CatalogString XliffStorage::targetWithTags(DocPosition pos) const
{
return catalogString(unitForPos(pos.entry), DocPosition::Target);
}
CatalogString XliffStorage::sourceWithTags(DocPosition pos) const
{
return catalogString(unitForPos(pos.entry), DocPosition::Source);
}
static QString genericContent(QDomElement elem, bool nonbin)
{
return nonbin?content(elem):elem.firstChildElement(QStringLiteral("external-file")).attribute(QStringLiteral("href"));
}
QString XliffStorage::source(const DocPosition& pos) const
{
return genericContent(sourceForPos(pos.entry),pos.entry
if (targetEl.isNull())
{
QDomNode unitEl=unitForPos(pos.entry);
QDomNode refNode=unitEl.firstChildElement(QStringLiteral("seg-source"));//obey standard
if (refNode.isNull()) refNode=unitEl.firstChildElement(binsourcesource[pos.entry
if (arg.isEmpty()) return; //means we were called just to add tag
if (pos.entry>=size())
{
QDomElement ef=targetEl.firstChildElement(QStringLiteral("external-file"));
if (ef.isNull())
ef=targetEl.appendChild(m_doc.createElement(QStringLiteral("external-file"))).toElement();
ef.setAttribute(QStringLiteral("href"),arg);
return;
}
ContentEditingData data(pos.offset,arg);
content(targetEl,&data);
}
void XliffStorage::targetInsertTag(const DocPosition& pos, const InlineTag& tag)
{
targetInsert(pos,QString()); //adds if needed
ContentEditingData data(tag.start,tag);
content(targetForPos(pos.entry),&data);
}
InlineTag XliffStorage::targetDeleteTag(const DocPosition& pos)
{
ContentEditingData data(pos.offset);
content(targetForPos(pos.entry),&data);
if (data.tags[0].end==-1) data.tags[0].end=data.tags[0].start;
return data.tags.first();
}
void XliffStorage::setTarget(const DocPosition& pos, const QString& arg)
{
Q_UNUSED(pos);
Q_UNUSED(arg);
//TODO
}
QVector XliffStorage::altTrans(const DocPosition& pos) const
{
QVector result;
QDomElement elem = unitForPos(pos.entry).firstChildElement(QStringLiteral("alt-trans"));
while (!elem.isNull())
{
AltTrans aTrans;
aTrans.source=catalogString(elem, DocPosition::Source);
aTrans.target=catalogString(elem, DocPosition::Target);
aTrans.phase=elem.attribute(QStringLiteral("phase-name"));
aTrans.origin=elem.attribute(QStringLiteral("origin"));
aTrans.score=elem.attribute(QStringLiteral("match-quality")).toInt();
aTrans.lang=elem.firstChildElement(QStringLiteral("target")).attribute(QStringLiteral("xml:lang"));
const char* const types[]={
"proposal",
"previous-version",
"rejected",
"reference",
"accepted"
};
QString typeStr=elem.attribute(QStringLiteral("alttranstype"));
int i=-1;
while (++i XliffStorage::allPhases() const
{
QList result;
QDomElement file=m_doc.elementsByTagName(QStringLiteral("file")).at(0).toElement();
QDomElement header=file.firstChildElement(QStringLiteral("header"));
QDomElement phasegroup=header.firstChildElement(QStringLiteral("phase-group"));
QDomElement phaseElem=phasegroup.firstChildElement(QStringLiteral("phase"));
while (!phaseElem.isNull())
{
result.append(phaseFromElement(phaseElem));
phaseElem=phaseElem.nextSiblingElement(QStringLiteral("phase"));
}
return result;
}
Phase XliffStorage::phase(const QString& name) const
{
QDomElement phasegroup;
QDomElement phaseElem=phaseElement(m_doc,name,phasegroup);
return phaseFromElement(phaseElem);
}
QMap XliffStorage::allTools() const
{
QMap result;
QDomElement file=m_doc.elementsByTagName(QStringLiteral("file")).at(0).toElement();
QDomElement header=file.firstChildElement(QStringLiteral("header"));
QDomElement toolElem=header.firstChildElement(QStringLiteral("tool"));
while (!toolElem.isNull())
{
Tool tool;
tool.tool =toolElem.attribute(QStringLiteral("tool-id"));
tool.name =toolElem.attribute(QStringLiteral("tool-name"));
tool.version =toolElem.attribute(QStringLiteral("tool-version"));
tool.company =toolElem.attribute(QStringLiteral("tool-company"));
result.insert(tool.tool, tool);
toolElem=toolElem.nextSiblingElement(QStringLiteral("tool"));
}
return result;
}
QStringList XliffStorage::sourceFiles(const DocPosition& pos) const
{
QStringList result;
QDomElement elem = unitForPos(pos.entry).firstChildElement(QStringLiteral("context-group"));
while (!elem.isNull())
{
if (elem.attribute(QStringLiteral("purpose")).contains(QLatin1String("location")))
{
QDomElement context = elem.firstChildElement(QStringLiteral("context"));
while (!context.isNull())
{
QString sourcefile;
QString linenumber;
const QString contextType = context.attribute(QStringLiteral("context-type"));
if (contextType == QLatin1String("sourcefile"))
sourcefile = context.text();
else if (contextType == QLatin1String("linenumber"))
linenumber = context.text();
if (!( sourcefile.isEmpty() && linenumber.isEmpty() ))
result.append(sourcefile % ':' % linenumber);
context=context.nextSiblingElement(QStringLiteral("context"));
}
}
elem=elem.nextSiblingElement(QStringLiteral("context-group"));
}
//qSort(result);
return result;
}
static void initNoteFromElement(Note& note, QDomElement elem)
{
note.content=elem.text();
note.from=elem.attribute(QStringLiteral("from"));
note.lang=elem.attribute(QStringLiteral("xml:lang"));
if (elem.attribute(QStringLiteral("annotates"))==QLatin1String("source"))
note.annotates=Note::Source;
else if (elem.attribute(QStringLiteral("annotates"))==QLatin1String("target"))
note.annotates=Note::Target;
bool ok;
note.priority=elem.attribute(QStringLiteral("priority")).toInt(&ok);
if (!ok) note.priority=0;
}
QVector XliffStorage::notes(const DocPosition& pos) const
{
QList result;
QDomElement elem = entries.at(m_map.at(pos.entry)).firstChildElement(NOTE);
while (!elem.isNull())
{
Note note;
initNoteFromElement(note,elem);
result.append(note);
elem=elem.nextSiblingElement(NOTE);
}
qSort(result);
return result.toVector();
}
QVector XliffStorage::developerNotes(const DocPosition& pos) const
{
Q_UNUSED(pos);
//TODO
return QVector();
}
Note XliffStorage::setNote(DocPosition pos, const Note& note)
{
//qCWarning(LOKALIZE_LOG)< result;
QDomNodeList notes=m_doc.elementsByTagName(NOTE);
int i=notes.size();
while (--i>=0)
{
QString from=notes.at(i).toElement().attribute(QStringLiteral("from"));
if (!from.isEmpty())
result.insert(from);
}
return result.toList();
}
QVector phaseNotes(QDomDocument m_doc, const QString& phasename, bool remove=false)
{
QVector result;
QDomElement phasegroup;
QDomElement phaseElem=phaseElement(m_doc,phasename,phasegroup);
QDomElement noteElem=phaseElem.firstChildElement(NOTE);
while (!noteElem.isNull())
{
Note note;
initNoteFromElement(note,noteElem);
result.append(note);
QDomElement old=noteElem;
noteElem=noteElem.nextSiblingElement(NOTE);
if (remove) phaseElem.removeChild(old);
}
return result;
}
QVector XliffStorage::phaseNotes(const QString& phasename) const
{
return ::phaseNotes(m_doc, phasename, false);
}
QVector XliffStorage::setPhaseNotes(const QString& phasename, QVector notes)
{
QVector result=::phaseNotes(m_doc, phasename, true);
QDomElement phasegroup;
QDomElement phaseElem=phaseElement(m_doc,phasename,phasegroup);
foreach(const Note& note, notes)
{
QDomElement elem=phaseElem.appendChild(m_doc.createElement(NOTE)).toElement();
elem.appendChild(m_doc.createTextNode(note.content));
if (!note.from.isEmpty()) elem.setAttribute(QStringLiteral("from"),note.from);
if (note.priority) elem.setAttribute(QStringLiteral("priority"),note.priority);
}
return result;
}
QString XliffStorage::setPhase(const DocPosition& pos, const QString& phase)
{
QString PHASENAME=QStringLiteral("phase-name");
targetInsert(pos,QString()); //adds if needed
QDomElement target=targetForPos(pos.entry);
QString result=target.attribute(PHASENAME);
if (phase.isEmpty())
target.removeAttribute(PHASENAME);
else if (phase!=result)
target.setAttribute(PHASENAME,phase);
return result;
}
QString XliffStorage::phase(const DocPosition& pos) const
{
QDomElement target=targetForPos(pos.entry);
return target.attribute(QStringLiteral("phase-name"));
}
QStringList XliffStorage::context(const DocPosition& pos) const
{
Q_UNUSED(pos);
//TODO
return QStringList(QString());
}
QStringList XliffStorage::matchData(const DocPosition& pos) const
{
Q_UNUSED(pos);
return QStringList();
}
QString XliffStorage::id(const DocPosition& pos) const
{
return unitForPos(pos.entry).attribute(QStringLiteral("id"));
}
bool XliffStorage::isPlural(const DocPosition& pos) const
{
return m_plurals.contains(pos.entry);
}
/*
bool XliffStorage::isApproved(const DocPosition& pos) const
{
return entries.at(m_map.at(pos.entry)).toElement().attribute("approved")=="yes";
}
void XliffStorage::setApproved(const DocPosition& pos, bool approved)
{
static const char* const noyes[]={"no","yes"};
entries.at(m_map.at(pos.entry)).toElement().setAttribute("approved",noyes[approved]);
}
*/
static const QString xliff_states[]={
U("new"), U("needs-translation"), U("needs-l10n"), U("needs-adaptation"), U("translated"),
U("needs-review-translation"), U("needs-review-l10n"), U("needs-review-adaptation"),
U("final"), U("signed-off")};
TargetState stringToState(const QString& state)
{
int i=sizeof(xliff_states)/sizeof(QString);
while (--i>0 && state!=xliff_states[i])
;
return TargetState(i);
}
TargetState XliffStorage::setState(const DocPosition& pos, TargetState state)
{
targetInsert(pos,QString()); //adds if needed
QDomElement target=targetForPos(pos.entry);
TargetState prev=stringToState(target.attribute(QStringLiteral("state")));
target.setAttribute(QStringLiteral("state"),xliff_states[state]);
unitForPos(pos.entry).setAttribute(QStringLiteral("approved"), noyes[state==SignedOff]);
return prev;
}
TargetState XliffStorage::state(const DocPosition& pos) const
{
QDomElement target=targetForPos(pos.entry);
if (!target.hasAttribute(QStringLiteral("state")) && unitForPos(pos.entry).attribute(QStringLiteral("approved"))==QLatin1String("yes"))
return SignedOff;
return stringToState(target.attribute(QStringLiteral("state")));
}
bool XliffStorage::isEmpty(const DocPosition& pos) const
{
ContentEditingData data(ContentEditingData::CheckLength);
return content(targetForPos(pos.entry),&data).isEmpty();
}
bool XliffStorage::isEquivTrans(const DocPosition& pos) const
{
return targetForPos(pos.entry).attribute(QStringLiteral("equiv-trans"))!=QLatin1String("no");
}
void XliffStorage::setEquivTrans(const DocPosition& pos, bool equivTrans)
{
targetForPos(pos.entry).setAttribute(QStringLiteral("equiv-trans"),noyes[equivTrans]);
}
QDomElement XliffStorage::unitForPos(int pos) const
{
if (pos= 3
-# define KDE_ISLIKELY( x ) __builtin_expect(!!(x),1)
-# define KDE_ISUNLIKELY( x ) __builtin_expect(!!(x),0)
-#else
-# define KDE_ISLIKELY( x ) ( x )
-# define KDE_ISUNLIKELY( x ) ( x )
-#endif
-#endif
diff --git a/src/editortab.cpp b/src/editortab.cpp
index 9e20614..21c1fba 100644
--- a/src/editortab.cpp
+++ b/src/editortab.cpp
@@ -1,1723 +1,1721 @@
/* ****************************************************************************
This file is part of Lokalize
Copyright (C) 2007-2014 by Nick Shaforostoff
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) version 3 or any later version
accepted by the membership of KDE e.V. (or its successor approved
by the membership of KDE e.V.), which shall act as a proxy
defined in Section 14 of version 3 of the license.
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, see .
**************************************************************************** */
#include "editortab.h"
#include "lokalize_debug.h"
#include "actionproxy.h"
#include "editorview.h"
#include "catalog.h"
#include "pos.h"
#include "cmd.h"
#include "completionstorage.h"
#ifndef NOKDE
#define WEBQUERY_ENABLE
#endif
//views
#include "msgctxtview.h"
#include "alttransview.h"
#include "mergeview.h"
#include "cataloglistview.h"
#include "glossaryview.h"
#ifdef WEBQUERY_ENABLE
#include "webqueryview.h"
#endif
#include "tmview.h"
#include "binunitsview.h"
#include "phaseswindow.h"
#include "projectlocal.h"
#include "project.h"
#include "prefs.h"
#include "languagelistmodel.h"
-#include "kdemacros.h"
#ifndef NOKDE
#include
-#include
#include
#include
#include
#include
#include
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
EditorTab::EditorTab(QWidget* parent, bool valid)
: LokalizeSubwindowBase2(parent)
, m_project(Project::instance())
, m_catalog(new Catalog(this))
, m_view(new EditorView(this,m_catalog/*,new keyEventHandler(this,m_catalog)*/))
#ifndef NOKDE
, m_sonnetDialog(0)
, m_spellcheckStartUndoIndex(0)
, m_spellcheckStop(false)
#endif
, m_currentIsApproved(true)
, m_currentIsUntr(true)
, m_fullPathShown(false)
#ifndef NOKDE
, m_doReplaceCalled(false)
, m_find(0)
, m_replace(0)
#endif
, m_syncView(0)
, m_syncViewSecondary(0)
#ifndef NOKDE
, m_valid(valid)
, m_dbusId(-1)
#endif
{
//QTime chrono;chrono.start();
setAcceptDrops(true);
setCentralWidget(m_view);
setupStatusBar(); //--NOT called from initLater() !
setupActions();
#ifndef NOKDE
dbusObjectPath();
#endif
connect(m_view, SIGNAL(signalChanged(uint)), this, SLOT(msgStrChanged())); msgStrChanged();
connect(SettingsController::instance(),SIGNAL(generalSettingsChanged()),m_view, SLOT(settingsChanged()));
connect(m_view->tabBar(),&QTabBar::currentChanged,this,&EditorTab::switchForm);
connect(m_view, SIGNAL(gotoEntryRequested(DocPosition)),this,SLOT(gotoEntry(DocPosition)));
connect(m_view, SIGNAL(tmLookupRequested(DocPosition::Part,QString)), this, SLOT(lookupSelectionInTranslationMemory()));
connect(this, SIGNAL(fileOpened()), this, SLOT(indexWordsForCompletion()),Qt::QueuedConnection);
connect (m_catalog,&Catalog::signalFileAutoSaveFailed,this,&EditorTab::fileAutoSaveFailedWarning);
//defer some work to make window appear earlier (~200 msec on my Core Duo)
//QTimer::singleShot(0,this,SLOT(initLater()));
//qCWarning(LOKALIZE_LOG)<isEmpty())
{
emit fileAboutToBeClosed();
emit fileClosed();
emit fileClosed(currentFile());
}
#ifndef NOKDE
ids.removeAll(m_dbusId);
#endif
}
void EditorTab::setupStatusBar()
{
statusBarItems.insert(ID_STATUS_CURRENT,i18nc("@info:status message entry","Current: %1",0));
statusBarItems.insert(ID_STATUS_TOTAL,i18nc("@info:status message entries","Total: %1",0));
statusBarItems.insert(ID_STATUS_FUZZY,i18nc("@info:status message entries\n'fuzzy' in gettext terminology","Not ready: %1",0));
statusBarItems.insert(ID_STATUS_UNTRANS,i18nc("@info:status message entries","Untranslated: %1",0));
statusBarItems.insert(ID_STATUS_ISFUZZY,QString());
connect(m_catalog,&Catalog::signalNumberOfFuzziesChanged,this,&EditorTab::numberOfFuzziesChanged);
connect(m_catalog,&Catalog::signalNumberOfEmptyChanged,this,&EditorTab::numberOfUntranslatedChanged);
}
#ifndef NOKDE
void LokalizeSubwindowBase::reflectNonApprovedCount(int count, int total)
{
QString text=i18nc("@info:status message entries\n'fuzzy' in gettext terminology","Not ready: %1", count);
if (count && total)
text+=i18nc("percentages in statusbar", " (%1%)", int(100.0*count/total));
statusBarItems.insert(ID_STATUS_FUZZY,text);
}
void LokalizeSubwindowBase::reflectUntranslatedCount(int count, int total)
{
QString text=i18nc("@info:status message entries","Untranslated: %1", count);
if (count && total)
text+=i18nc("percentages in statusbar", " (%1%)", int(100.0*count/total));
statusBarItems.insert(ID_STATUS_UNTRANS,text);
}
#endif
void EditorTab::numberOfFuzziesChanged()
{
reflectNonApprovedCount(m_catalog->numberOfNonApproved(),m_catalog->numberOfEntries());
}
void EditorTab::numberOfUntranslatedChanged()
{
reflectUntranslatedCount(m_catalog->numberOfUntranslated(),m_catalog->numberOfEntries());
}
void EditorTab::setupActions()
{
//all operations that can be done after initial setup
//(via QTimer::singleShot) go to initLater()
setXMLFile(QStringLiteral("editorui.rc"));
QAction *action;
KActionCollection* ac=actionCollection();
KActionCategory* actionCategory;
KActionCategory* file=new KActionCategory(i18nc("@title actions category","File"), ac);
KActionCategory* nav=new KActionCategory(i18nc("@title actions category","Navigation"), ac);
KActionCategory* edit=new KActionCategory(i18nc("@title actions category","Editing"), ac);
KActionCategory* sync1=new KActionCategory(i18n("Synchronization 1"), ac);
KActionCategory* sync2=new KActionCategory(i18n("Synchronization 2"), ac);
KActionCategory* tm=new KActionCategory(i18n("Translation Memory"), ac);
KActionCategory* glossary=new KActionCategory(i18nc("@title actions category","Glossary"), actionCollection());
//KActionCategory* tools=new KActionCategory(i18nc("@title actions category","Tools"), actionCollection());
#ifndef Q_OS_DARWIN
QLocale::Language systemLang = QLocale::system().language();
#endif
//BEGIN dockwidgets
int i=0;
QVector altactions(ALTTRANS_SHORTCUTS);
Qt::Key altlist[ALTTRANS_SHORTCUTS]=
{
Qt::Key_1,
Qt::Key_2,
Qt::Key_3,
Qt::Key_4,
Qt::Key_5,
Qt::Key_6,
Qt::Key_7,
Qt::Key_8,
Qt::Key_9
};
QAction* altaction;
for (i=0;iaddAction(QStringLiteral("alttrans_insert_%1").arg(i));
ac->setDefaultShortcut(altaction, QKeySequence(Qt::ALT+altlist[i]));
altaction->setText(i18nc("@action:inmenu","Insert alternate translation #%1",QString::number(i)));
altactions[i]=altaction;
}
m_altTransView = new AltTransView(this,m_catalog,altactions);
addDockWidget(Qt::BottomDockWidgetArea, m_altTransView);
actionCollection()->addAction( QStringLiteral("showmsgiddiff_action"), m_altTransView->toggleViewAction() );
connect (this,SIGNAL(signalNewEntryDisplayed(DocPosition)),m_altTransView,SLOT(slotNewEntryDisplayed(DocPosition)));
connect (m_altTransView,SIGNAL(textInsertRequested(QString)),m_view,SLOT(insertTerm(QString)));
connect (m_altTransView,SIGNAL(refreshRequested()),m_view,SLOT(gotoEntry()),Qt::QueuedConnection);
connect (m_catalog,SIGNAL(signalFileLoaded()),m_altTransView,SLOT(fileLoaded()));
m_syncView = new MergeView(this,m_catalog,true);
addDockWidget(Qt::BottomDockWidgetArea, m_syncView);
sync1->addAction( QStringLiteral("showmergeview_action"), m_syncView->toggleViewAction() );
connect (this,SIGNAL(signalNewEntryDisplayed(DocPosition)),m_syncView,SLOT(slotNewEntryDisplayed(DocPosition)));
connect (m_catalog,SIGNAL(signalFileLoaded()),m_syncView,SLOT(cleanup()));
connect (m_syncView,SIGNAL(gotoEntry(DocPosition,int)),
this,SLOT(gotoEntry(DocPosition,int)));
m_syncViewSecondary = new MergeView(this,m_catalog,false);
addDockWidget(Qt::BottomDockWidgetArea, m_syncViewSecondary);
sync2->addAction( QStringLiteral("showmergeviewsecondary_action"), m_syncViewSecondary->toggleViewAction() );
connect (this,SIGNAL(signalNewEntryDisplayed(DocPosition)),m_syncViewSecondary,SLOT(slotNewEntryDisplayed(DocPosition)));
connect (m_catalog,SIGNAL(signalFileLoaded()),m_syncViewSecondary,SLOT(cleanup()));
connect (m_catalog,SIGNAL(signalFileLoaded(QString)),m_syncViewSecondary,SLOT(mergeOpen(QString)),Qt::QueuedConnection);
connect (m_syncViewSecondary,SIGNAL(gotoEntry(DocPosition,int)),
this,SLOT(gotoEntry(DocPosition,int)));
m_transUnitsView = new CatalogView(this,m_catalog);
addDockWidget(Qt::LeftDockWidgetArea, m_transUnitsView);
actionCollection()->addAction( QStringLiteral("showcatalogtreeview_action"), m_transUnitsView->toggleViewAction() );
connect (this,SIGNAL(signalNewEntryDisplayed(DocPosition)),m_transUnitsView,SLOT(slotNewEntryDisplayed(DocPosition)));
connect (m_transUnitsView,SIGNAL(gotoEntry(DocPosition,int)),this,SLOT(gotoEntry(DocPosition,int)));
connect (m_transUnitsView,&CatalogView::escaped,this,&EditorTab::setProperFocus);
connect (m_syncView,&MergeView::mergeCatalogPointerChanged, m_transUnitsView, &CatalogView::setMergeCatalogPointer);
m_notesView = new MsgCtxtView(this,m_catalog);
addDockWidget(Qt::LeftDockWidgetArea, m_notesView);
actionCollection()->addAction( QStringLiteral("showmsgctxt_action"), m_notesView->toggleViewAction() );
connect(m_catalog,SIGNAL(signalFileLoaded()),m_notesView,SLOT(cleanup()));
connect(m_notesView,SIGNAL(srcFileOpenRequested(QString,int)),this,SLOT(dispatchSrcFileOpenRequest(QString,int)));
connect(m_view, SIGNAL(signalChanged(uint)), m_notesView, SLOT(removeErrorNotes()));
connect(m_notesView,&MsgCtxtView::escaped,this,&EditorTab::setProperFocus);
action=edit->addAction(QStringLiteral("edit_addnote"),m_notesView,SLOT(addNoteUI()));
//action->setShortcut(Qt::CTRL+glist[i]);
action->setText(i18nc("@action:inmenu","Add a note"));
QVector tmactions(TM_SHORTCUTS);
Qt::Key tmlist[TM_SHORTCUTS]=
{
Qt::Key_1,
Qt::Key_2,
Qt::Key_3,
Qt::Key_4,
Qt::Key_5,
Qt::Key_6,
Qt::Key_7,
Qt::Key_8,
Qt::Key_9,
Qt::Key_0
};
QAction* tmaction;
for (i=0;isetVisible(false);
tmaction=tm->addAction(QStringLiteral("tmquery_insert_%1").arg(i));
ac->setDefaultShortcut(tmaction, QKeySequence(Qt::CTRL+tmlist[i]));
tmaction->setText(i18nc("@action:inmenu","Insert TM suggestion #%1",QString::number(i+1)));
tmactions[i]=tmaction;
}
#ifndef Q_OS_DARWIN
if (systemLang==QLocale::Czech) ac->setDefaultShortcuts(tmactions[0], QList()<addAction( QStringLiteral("showtmqueryview_action"), _tmView->toggleViewAction() );
connect (_tmView,SIGNAL(refreshRequested()),m_view,SLOT(gotoEntry()),Qt::QueuedConnection);
connect (_tmView,SIGNAL(refreshRequested()),this,SLOT(msgStrChanged()),Qt::QueuedConnection);
connect (_tmView,SIGNAL(textInsertRequested(QString)),m_view,SLOT(insertTerm(QString)));
connect (_tmView,SIGNAL(fileOpenRequested(QString,QString,QString)),this,SIGNAL(fileOpenRequested(QString,QString,QString)));
connect (this,SIGNAL(fileAboutToBeClosed()),m_catalog,SLOT(flushUpdateDBBuffer()));
connect (this,SIGNAL(signalNewEntryDisplayed(DocPosition)),m_catalog,SLOT(flushUpdateDBBuffer()));
connect (this,SIGNAL(signalNewEntryDisplayed(DocPosition)),_tmView,SLOT(slotNewEntryDisplayed(DocPosition))); //do this after flushUpdateDBBuffer
QVector gactions(GLOSSARY_SHORTCUTS);
Qt::Key glist[GLOSSARY_SHORTCUTS]=
{
Qt::Key_E,
Qt::Key_H,
// Qt::Key_G,
// Qt::Key_I,
// Qt::Key_J,
// Qt::Key_K,
Qt::Key_K,
Qt::Key_P,
Qt::Key_N,
// Qt::Key_Q,
// Qt::Key_R,
// Qt::Key_U,
// Qt::Key_V,
// Qt::Key_W,
// Qt::Key_X,
Qt::Key_Y,
// Qt::Key_Z,
Qt::Key_BraceLeft,
Qt::Key_BraceRight,
Qt::Key_Semicolon,
Qt::Key_Apostrophe
};
QAction* gaction;
// int i=0;
for (i=0;isetVisible(false);
gaction=glossary->addAction(QStringLiteral("glossary_insert_%1").arg(i));
ac->setDefaultShortcut(gaction, QKeySequence(Qt::CTRL+glist[i]));
gaction->setText(i18nc("@action:inmenu","Insert term translation #%1",QString::number(i)));
gactions[i]=gaction;
}
GlossaryNS::GlossaryView* _glossaryView = new GlossaryNS::GlossaryView(this,m_catalog,gactions);
addDockWidget(Qt::BottomDockWidgetArea, _glossaryView);
glossary->addAction( QStringLiteral("showglossaryview_action"), _glossaryView->toggleViewAction() );
connect (this,&EditorTab::signalNewEntryDisplayed,_glossaryView,&GlossaryNS::GlossaryView::slotNewEntryDisplayed);
connect (_glossaryView,SIGNAL(termInsertRequested(QString)),m_view,SLOT(insertTerm(QString)));
gaction = glossary->addAction(QStringLiteral("glossary_define"),this,SLOT(defineNewTerm()));
gaction->setText(i18nc("@action:inmenu","Define new term"));
_glossaryView->addAction(gaction);
_glossaryView->setContextMenuPolicy( Qt::ActionsContextMenu);
BinUnitsView* binUnitsView=new BinUnitsView(m_catalog,this);
addDockWidget(Qt::BottomDockWidgetArea, binUnitsView);
edit->addAction( QStringLiteral("showbinunitsview_action"), binUnitsView->toggleViewAction() );
connect(m_view,&EditorView::binaryUnitSelectRequested,binUnitsView,&BinUnitsView::selectUnit);
//#ifdef WEBQUERY_ENABLE
#if 0
QVector wqactions(WEBQUERY_SHORTCUTS);
Qt::Key wqlist[WEBQUERY_SHORTCUTS]=
{
Qt::Key_1,
Qt::Key_2,
Qt::Key_3,
Qt::Key_4,
Qt::Key_5,
Qt::Key_6,
Qt::Key_7,
Qt::Key_8,
Qt::Key_9,
Qt::Key_0,
};
QAction* wqaction;
for (i=0;isetVisible(false);
wqaction=actionCollection()->addAction(QString("webquery_insert_%1").arg(i));
wqaction->setShortcut(Qt::CTRL+Qt::ALT+wqlist[i]);
//wqaction->setShortcut(Qt::META+wqlist[i]);
wqaction->setText(i18nc("@action:inmenu","Insert WebQuery result #%1",i));
wqactions[i]=wqaction;
}
WebQueryView* _webQueryView = new WebQueryView(this,m_catalog,wqactions);
addDockWidget(Qt::BottomDockWidgetArea, _webQueryView);
actionCollection()->addAction( QStringLiteral("showwebqueryview_action"), _webQueryView->toggleViewAction() );
connect (this,SIGNAL(signalNewEntryDisplayed(DocPosition)),_webQueryView,SLOT(slotNewEntryDisplayed(DocPosition)));
connect (_webQueryView,SIGNAL(textInsertRequested(QString)),m_view,SLOT(insertTerm(QString)));
#endif
//END dockwidgets
actionCategory=file;
// File
action=file->addAction(KStandardAction::Save,this, SLOT(saveFile()));
// action->setEnabled(false);
// connect (m_catalog,SIGNAL(cleanChanged(bool)),action,SLOT(setDisabled(bool)));
connect (m_catalog,SIGNAL(cleanChanged(bool)),this,SLOT(setModificationSign()));
file->addAction(KStandardAction::SaveAs,this, SLOT(saveFileAs()));
//action = KStandardAction::quit(qApp, SLOT(quit()), ac);
//action->setText(i18nc("@action:inmenu","Close all Lokalize windows"));
//KStandardAction::quit(kapp, SLOT(quit()), ac);
//KStandardAction::quit(this, SLOT(deleteLater()), ac);
#define ADD_ACTION_SHORTCUT_ICON(_name,_text,_shortcut,_icon)\
action = actionCategory->addAction(QStringLiteral(_name));\
action->setText(_text);\
action->setIcon(QIcon::fromTheme(QStringLiteral(_icon)));\
ac->setDefaultShortcut(action, QKeySequence( _shortcut ));
#define ADD_ACTION_SHORTCUT(_name,_text,_shortcut)\
action = actionCategory->addAction(QStringLiteral(_name));\
action->setText(_text);\
ac->setDefaultShortcut(action, QKeySequence( _shortcut ));
action = actionCategory->addAction(QStringLiteral("file_phases"));
action->setText(i18nc("@action:inmenu","Phases..."));
connect(action, SIGNAL(triggered()), SLOT(openPhasesWindow()));
ADD_ACTION_SHORTCUT("file_wordcount",i18nc("@action:inmenu","Word count"),Qt::CTRL+Qt::ALT+Qt::Key_C)
connect( action, SIGNAL(triggered(bool)), this, SLOT(displayWordCount()) );
ADD_ACTION_SHORTCUT("file_xliff2odf",i18nc("@action:inmenu","Merge translation into OpenDocument"),Qt::CTRL+Qt::Key_Backslash)
connect( action, SIGNAL(triggered(bool)), this, SLOT(mergeIntoOpenDocument()) );
connect( this, SIGNAL(xliffFileOpened(bool)), action, SLOT(setVisible(bool)) );
action->setVisible(false);
//Edit
actionCategory=edit;
action=edit->addAction(KStandardAction::Undo,this,SLOT(undo()));
connect(m_view->viewPort(),SIGNAL(undoRequested()),this,SLOT(undo()));
connect(m_catalog,SIGNAL(canUndoChanged(bool)),action,SLOT(setEnabled(bool)) );
action->setEnabled(false);
action=edit->addAction(KStandardAction::Redo,this,SLOT(redo()));
connect(m_view->viewPort(),SIGNAL(redoRequested()),this,SLOT(redo()));
connect(m_catalog,SIGNAL(canRedoChanged(bool)),action,SLOT(setEnabled(bool)) );
action->setEnabled(false);
action=nav->addAction(KStandardAction::Find,this,SLOT(find()));
action=nav->addAction(KStandardAction::FindNext,this,SLOT(findNext()));
action=nav->addAction(KStandardAction::FindPrev,this,SLOT(findPrev()));
action->setText(i18nc("@action:inmenu","Change searching direction"));
action=edit->addAction(KStandardAction::Replace,this,SLOT(replace()));
connect(m_view,SIGNAL(findRequested()), this,SLOT(find()));
connect(m_view,SIGNAL(findNextRequested()), this,SLOT(findNext()));
connect(m_view,SIGNAL(replaceRequested()), this,SLOT(replace()));
action = actionCategory->addAction(QStringLiteral("edit_approve"),
new KToolBarPopupAction(QIcon::fromTheme(QStringLiteral("approved")),
i18nc("@option:check whether message is marked as translated/reviewed/approved (depending on your role)","Approved"), this));
ac->setDefaultShortcut(action, QKeySequence(Qt::CTRL+Qt::Key_U));
action->setCheckable(true);
connect(action, SIGNAL(triggered()), m_view,SLOT(toggleApprovement()));
connect(m_view, SIGNAL(signalApprovedEntryDisplayed(bool)),this,SIGNAL(signalApprovedEntryDisplayed(bool)));
connect(this, &EditorTab::signalApprovedEntryDisplayed,action,&QAction::setChecked);
connect(this, SIGNAL(signalApprovedEntryDisplayed(bool)),this,SLOT(msgStrChanged()),Qt::QueuedConnection);
m_approveAction=action;
#ifdef NOKDE
QMenu* am=new QMenu(i18nc("@option:check whether message is marked as translated/reviewed/approved (depending on your role)","State"),this);
action=am->menuAction();
ac->addAction(QStringLiteral("edit_state"),action);
#endif
m_stateAction=action;
connect(Project::local(), SIGNAL(configChanged()), SLOT(setApproveActionTitle()));
connect(m_catalog, SIGNAL(activePhaseChanged()), SLOT(setApproveActionTitle()));
connect(m_stateAction->menu(), SIGNAL(aboutToShow()),this,SLOT(showStatesMenu()));
connect(m_stateAction->menu(), SIGNAL(triggered(QAction*)),this,SLOT(setState(QAction*)));
action = actionCategory->addAction(QStringLiteral("edit_approve_go_fuzzyUntr"));
action->setText(i18nc("@action:inmenu","Approve and go to next"));
connect(action, SIGNAL(triggered()), SLOT(toggleApprovementGotoNextFuzzyUntr()));
m_approveAndGoAction = action;
setApproveActionTitle();
action = actionCategory->addAction(QStringLiteral("edit_nonequiv"),m_view,SLOT(setEquivTrans(bool)));
action->setText(i18nc("@action:inmenu","Equivalent translation"));
action->setCheckable(true);
connect(this, SIGNAL(signalEquivTranslatedEntryDisplayed(bool)),action,SLOT(setChecked(bool)));
#ifndef Q_OS_DARWIN
int copyShortcut=Qt::CTRL+Qt::Key_Space;
- if (KDE_ISUNLIKELY( systemLang==QLocale::Korean
+ if (Q_UNLIKELY( systemLang==QLocale::Korean
|| systemLang==QLocale::Japanese
|| systemLang==QLocale::Chinese
))
copyShortcut=Qt::ALT+Qt::Key_Space;
#else
int copyShortcut=Qt::META+Qt::Key_Space;
#endif
ADD_ACTION_SHORTCUT_ICON("edit_msgid2msgstr",i18nc("@action:inmenu","Copy source to target"),copyShortcut,"msgid2msgstr")
connect(action, SIGNAL(triggered(bool)), m_view->viewPort(),SLOT(source2target()));
ADD_ACTION_SHORTCUT("edit_unwrap-target",i18nc("@action:inmenu","Unwrap target"),Qt::CTRL+Qt::Key_I)
connect(action, SIGNAL(triggered(bool)), m_view,SLOT(unwrap()));
action=edit->addAction(QStringLiteral("edit_clear-target"),m_view->viewPort(),SLOT(removeTargetSubstring()));
ac->setDefaultShortcut(action, QKeySequence(Qt::CTRL+Qt::Key_D));
action->setText(i18nc("@action:inmenu","Clear"));
action=edit->addAction(QStringLiteral("edit_tagmenu"),m_view->viewPort(),SLOT(tagMenu()));
ac->setDefaultShortcut(action, QKeySequence(Qt::CTRL+Qt::Key_T));
action->setText(i18nc("@action:inmenu","Insert Tag"));
action=edit->addAction(QStringLiteral("edit_tagimmediate"),m_view->viewPort(),SLOT(tagImmediate()));
ac->setDefaultShortcut(action, QKeySequence(Qt::CTRL+Qt::Key_M));
action->setText(i18nc("@action:inmenu","Insert Next Tag"));
#ifndef NOKDE
action=edit->addAction(QStringLiteral("edit_completion"),m_view,SIGNAL(doExplicitCompletion()));
ac->setDefaultShortcut(action, QKeySequence(Qt::CTRL+Qt::ALT+Qt::Key_Space));
action->setText(i18nc("@action:inmenu","Completion"));
action=edit->addAction(QStringLiteral("edit_spellreplace"),m_view->viewPort(),SLOT(spellReplace()));
ac->setDefaultShortcut(action, QKeySequence(Qt::CTRL+Qt::Key_Equal));
action->setText(i18nc("@action:inmenu","Replace with best spellcheck suggestion"));
#endif
// action = ac->addAction("glossary_define",m_view,SLOT(defineNewTerm()));
// action->setText(i18nc("@action:inmenu","Define new term"));
// Go
actionCategory=nav;
action=nav->addAction(KStandardAction::Next,this, SLOT(gotoNext()));
action->setText(i18nc("@action:inmenu entry","&Next"));
connect( this, &EditorTab::signalLastDisplayed, action, &QAction::setDisabled);
connect(m_view->viewPort(),SIGNAL(gotoNextRequested()),this,SLOT(gotoNext()));
action=nav->addAction(KStandardAction::Prior,this, SLOT(gotoPrev()));
action->setText(i18nc("@action:inmenu entry","&Previous"));
connect( this, &EditorTab::signalFirstDisplayed, action, &QAction::setDisabled);
connect(m_view->viewPort(),SIGNAL(gotoPrevRequested()),this,SLOT(gotoPrev()));
action=nav->addAction(KStandardAction::FirstPage,this, SLOT(gotoFirst()));
connect(m_view->viewPort(),SIGNAL(gotoFirstRequested()),this,SLOT(gotoFirst()));
action->setText(i18nc("@action:inmenu","&First Entry"));
action->setShortcut(QKeySequence(Qt::CTRL+Qt::ALT+Qt::Key_Home));
connect( this, &EditorTab::signalFirstDisplayed, action, &QAction::setDisabled);
action=nav->addAction(KStandardAction::LastPage,this, SLOT(gotoLast()));
connect(m_view->viewPort(),SIGNAL(gotoLastRequested()),this,SLOT(gotoLast()));
action->setText(i18nc("@action:inmenu","&Last Entry"));
action->setShortcut(QKeySequence(Qt::CTRL+Qt::ALT+Qt::Key_End));
connect( this, &EditorTab::signalLastDisplayed,action,&QAction::setDisabled);
action=nav->addAction(KStandardAction::GotoPage,this, SLOT(gotoEntry()));
ac->setDefaultShortcut(action, QKeySequence(Qt::CTRL+Qt::Key_G));
action->setText(i18nc("@action:inmenu","Entry by number"));
ADD_ACTION_SHORTCUT_ICON("go_prev_fuzzy",i18nc("@action:inmenu\n'not ready' means 'fuzzy' in gettext terminology","Previous non-empty but not ready"),Qt::CTRL+Qt::Key_PageUp,"prevfuzzy")
connect( action, SIGNAL(triggered(bool)), this, SLOT(gotoPrevFuzzy()) );
connect( m_view->viewPort(), SIGNAL(gotoPrevFuzzyRequested()), this, SLOT(gotoPrevFuzzy()) );
connect( this, &EditorTab::signalPriorFuzzyAvailable,action,&QAction::setEnabled);
ADD_ACTION_SHORTCUT_ICON("go_next_fuzzy",i18nc("@action:inmenu\n'not ready' means 'fuzzy' in gettext terminology","Next non-empty but not ready"),Qt::CTRL+Qt::Key_PageDown,"nextfuzzy")
connect( action, SIGNAL(triggered(bool)), this, SLOT(gotoNextFuzzy()) );
connect( m_view->viewPort(), SIGNAL(gotoNextFuzzyRequested()), this, SLOT(gotoNextFuzzy()) );
connect( this, &EditorTab::signalNextFuzzyAvailable,action,&QAction::setEnabled);
ADD_ACTION_SHORTCUT_ICON("go_prev_untrans",i18nc("@action:inmenu","Previous untranslated"),Qt::ALT+Qt::Key_PageUp,"prevuntranslated")
connect( action, SIGNAL(triggered(bool)), this, SLOT(gotoPrevUntranslated()));
connect( m_view->viewPort(), SIGNAL(gotoPrevUntranslatedRequested()), this, SLOT(gotoPrevUntranslated()) );
connect( this, &EditorTab::signalPriorUntranslatedAvailable,action,&QAction::setEnabled);
ADD_ACTION_SHORTCUT_ICON("go_next_untrans",i18nc("@action:inmenu","Next untranslated"),Qt::ALT+Qt::Key_PageDown,"nextuntranslated")
connect( action, SIGNAL(triggered(bool)), this, SLOT(gotoNextUntranslated()));
connect( m_view->viewPort(), SIGNAL(gotoNextUntranslatedRequested()), this, SLOT(gotoNextUntranslated()) );
connect( this, &EditorTab::signalNextUntranslatedAvailable,action,&QAction::setEnabled);
ADD_ACTION_SHORTCUT_ICON("go_prev_fuzzyUntr",i18nc("@action:inmenu\n'not ready' means 'fuzzy' in gettext terminology","Previous not ready"),Qt::CTRL+Qt::SHIFT/*ALT*/+Qt::Key_PageUp,"prevfuzzyuntrans")
connect( action, SIGNAL(triggered(bool)), this, SLOT(gotoPrevFuzzyUntr()) );
connect( m_view->viewPort(), SIGNAL(gotoPrevFuzzyUntrRequested()), this, SLOT(gotoPrevFuzzyUntr()) );
connect( this, &EditorTab::signalPriorFuzzyOrUntrAvailable,action,&QAction::setEnabled);
ADD_ACTION_SHORTCUT_ICON("go_next_fuzzyUntr",i18nc("@action:inmenu\n'not ready' means 'fuzzy' in gettext terminology","Next not ready"),Qt::CTRL+Qt::SHIFT+Qt::Key_PageDown,"nextfuzzyuntrans")
connect( action, SIGNAL(triggered(bool)), this, SLOT(gotoNextFuzzyUntr()) );
connect( m_view->viewPort(), SIGNAL(gotoNextFuzzyUntrRequested()), this, SLOT(gotoNextFuzzyUntr()) );
connect( this, &EditorTab::signalNextFuzzyOrUntrAvailable,action,&QAction::setEnabled);
action=nav->addAction(QStringLiteral("go_focus_earch_line"),m_transUnitsView, SLOT(setFocus()));
ac->setDefaultShortcut(action, QKeySequence(Qt::CTRL+Qt::Key_L));
action->setText(i18nc("@action:inmenu","Focus the search line of Translation Units view"));
//Bookmarks
action=nav->addAction(KStandardAction::AddBookmark,m_view,SLOT(toggleBookmark(bool)));
//action = ac->addAction("bookmark_do");
action->setText(i18nc("@option:check","Bookmark message"));
action->setCheckable(true);
//connect(action, SIGNAL(triggered(bool)),m_view,SLOT(toggleBookmark(bool)));
connect( this, &EditorTab::signalBookmarkDisplayed,action,&QAction::setChecked);
action=nav->addAction(QStringLiteral("bookmark_prior"),this,SLOT(gotoPrevBookmark()));
action->setText(i18nc("@action:inmenu","Previous bookmark"));
connect( this, &EditorTab::signalPriorBookmarkAvailable,action,&QAction::setEnabled);
action=nav->addAction(QStringLiteral("bookmark_next"),this,SLOT(gotoNextBookmark()));
action->setText(i18nc("@action:inmenu","Next bookmark"));
connect( this, &EditorTab::signalNextBookmarkAvailable,action,&QAction::setEnabled);
//Tools
edit->addAction(KStandardAction::Spelling,this,SLOT(spellcheck()));
actionCategory=tm;
// xgettext: no-c-format
ADD_ACTION_SHORTCUT("tools_tm_batch",i18nc("@action:inmenu","Fill in all exact suggestions"),Qt::CTRL+Qt::ALT+Qt::Key_B)
connect( action, SIGNAL(triggered(bool)), _tmView, SLOT(slotBatchTranslate()) );
// xgettext: no-c-format
ADD_ACTION_SHORTCUT("tools_tm_batch_fuzzy",i18nc("@action:inmenu","Fill in all exact suggestions and mark as fuzzy"),Qt::CTRL+Qt::ALT+Qt::Key_N)
connect( action, SIGNAL(triggered(bool)), _tmView, SLOT(slotBatchTranslateFuzzy()) );
//MergeMode
action = sync1->addAction(QStringLiteral("merge_open"),m_syncView,SLOT(mergeOpen()));
action->setText(i18nc("@action:inmenu","Open file for sync/merge"));
action->setStatusTip(i18nc("@info:status","Open catalog to be merged into the current one / replicate base file changes to"));
action->setToolTip(action->statusTip());
action->setWhatsThis(action->statusTip());
m_syncView->addAction(action);
action = sync1->addAction(QStringLiteral("merge_prev"),m_syncView,SLOT(gotoPrevChanged()));
action->setText(i18nc("@action:inmenu","Previous different"));
action->setStatusTip(i18nc("@info:status","Previous entry which is translated differently in the file being merged, including empty translations in merge source"));
action->setToolTip(action->statusTip());
action->setWhatsThis(action->statusTip());
ac->setDefaultShortcut(action, QKeySequence(Qt::ALT+Qt::Key_Up));
connect( m_syncView, &MergeView::signalPriorChangedAvailable,action,&QAction::setEnabled);
m_syncView->addAction(action);
action = sync1->addAction(QStringLiteral("merge_next"),m_syncView,SLOT(gotoNextChanged()));
action->setText(i18nc("@action:inmenu","Next different"));
action->setStatusTip(i18nc("@info:status","Next entry which is translated differently in the file being merged, including empty translations in merge source"));
action->setToolTip(action->statusTip());
action->setWhatsThis(action->statusTip());
ac->setDefaultShortcut(action, QKeySequence(Qt::ALT+Qt::Key_Down));
connect( m_syncView, &MergeView::signalNextChangedAvailable,action,&QAction::setEnabled);
m_syncView->addAction(action);
action = sync1->addAction(QStringLiteral("merge_nextapproved"),m_syncView,SLOT(gotoNextChangedApproved()));
action->setText(i18nc("@action:inmenu","Next different approved"));
ac->setDefaultShortcut(action, QKeySequence(Qt::ALT+Qt::META+Qt::Key_Down));
connect( m_syncView, &MergeView::signalNextChangedAvailable,action,&QAction::setEnabled);
m_syncView->addAction(action);
action = sync1->addAction(QStringLiteral("merge_accept"),m_syncView,SLOT(mergeAccept()));
action->setText(i18nc("@action:inmenu","Copy from merging source"));
action->setEnabled(false);
ac->setDefaultShortcut(action, QKeySequence(Qt::ALT+Qt::Key_Return));
connect( m_syncView, &MergeView::signalEntryWithMergeDisplayed,action,&QAction::setEnabled);
m_syncView->addAction(action);
action = sync1->addAction(QStringLiteral("merge_acceptnew"),m_syncView,SLOT(mergeAcceptAllForEmpty()));
action->setText(i18nc("@action:inmenu","Copy all new translations"));
action->setStatusTip(i18nc("@info:status","This changes only empty and non-ready entries in base file"));
action->setToolTip(action->statusTip());
action->setWhatsThis(action->statusTip());
ac->setDefaultShortcut(action, QKeySequence(Qt::CTRL+Qt::ALT+Qt::Key_A));
connect( m_syncView, &MergeView::mergeCatalogAvailable,action,&QAction::setEnabled);
m_syncView->addAction(action);
//action->setShortcut(Qt::ALT+Qt::Key_E);
action = sync1->addAction(QStringLiteral("merge_back"),m_syncView,SLOT(mergeBack()));
action->setText(i18nc("@action:inmenu","Copy to merging source"));
connect( m_syncView, &MergeView::mergeCatalogAvailable,action,&QAction::setEnabled);
ac->setDefaultShortcut(action, QKeySequence(Qt::CTRL+Qt::ALT+Qt::Key_Return));
m_syncView->addAction(action);
//Secondary merge
action = sync2->addAction(QStringLiteral("mergesecondary_open"),m_syncViewSecondary,SLOT(mergeOpen()));
action->setText(i18nc("@action:inmenu","Open file for secondary sync"));
action->setStatusTip(i18nc("@info:status","Open catalog to be merged into the current one / replicate base file changes to"));
action->setToolTip(action->statusTip());
action->setWhatsThis(action->statusTip());
m_syncViewSecondary->addAction(action);
action = sync2->addAction(QStringLiteral("mergesecondary_prev"),m_syncViewSecondary,SLOT(gotoPrevChanged()));
action->setText(i18nc("@action:inmenu","Previous different"));
action->setStatusTip(i18nc("@info:status","Previous entry which is translated differently in the file being merged, including empty translations in merge source"));
action->setToolTip(action->statusTip());
action->setWhatsThis(action->statusTip());
connect( m_syncView, &MergeView::signalPriorChangedAvailable,action,&QAction::setEnabled);
m_syncViewSecondary->addAction(action);
action = sync2->addAction(QStringLiteral("mergesecondary_next"),m_syncViewSecondary,SLOT(gotoNextChanged()));
action->setText(i18nc("@action:inmenu","Next different"));
action->setStatusTip(i18nc("@info:status","Next entry which is translated differently in the file being merged, including empty translations in merge source"));
action->setToolTip(action->statusTip());
action->setWhatsThis(action->statusTip());
connect( m_syncView, &MergeView::signalNextChangedAvailable,action,&QAction::setEnabled);
m_syncViewSecondary->addAction(action);
action = sync2->addAction(QStringLiteral("mergesecondary_accept"),m_syncViewSecondary,SLOT(mergeAccept()));
action->setText(i18nc("@action:inmenu","Copy from merging source"));
connect( m_syncView, &MergeView::signalEntryWithMergeDisplayed,action,&QAction::setEnabled);
m_syncViewSecondary->addAction(action);
action = sync2->addAction(QStringLiteral("mergesecondary_acceptnew"),m_syncViewSecondary,SLOT(mergeAcceptAllForEmpty()));
action->setText(i18nc("@action:inmenu","Copy all new translations"));
action->setStatusTip(i18nc("@info:status","This changes only empty entries"));
action->setToolTip(action->statusTip());
action->setWhatsThis(action->statusTip());
m_syncViewSecondary->addAction(action);
action = sync2->addAction(QStringLiteral("mergesecondary_back"),m_syncViewSecondary,SLOT(mergeBack()));
action->setText(i18nc("@action:inmenu","Copy to merging source"));
m_syncViewSecondary->addAction(action);
}
void EditorTab::setProperFocus()
{
m_view->setProperFocus();
}
void EditorTab::hideDocks()
{
if (m_transUnitsView->isFloating())
m_transUnitsView->hide();
}
void EditorTab::showDocks()
{
return;
if (m_transUnitsView->isFloating())
m_transUnitsView->show();
}
void EditorTab::setProperCaption(QString title, bool modified)
{
if (m_catalog->autoSaveRecovered()) title+=' '+i18nc("editor tab name","(recovered)");
setWindowTitle(title+QStringLiteral(" [*]"));
setWindowModified(modified);
}
void EditorTab::setFullPathShown(bool fullPathShown)
{
m_fullPathShown=fullPathShown;
updateCaptionPath();
setModificationSign();
}
void EditorTab::setModificationSign()
{
bool clean=m_catalog->isClean() && !m_syncView->isModified() && !m_syncViewSecondary->isModified();
setProperCaption(_captionPath,!clean);
}
void EditorTab::updateCaptionPath()
{
QString url=m_catalog->url();
if (!m_project->isLoaded())
{
_captionPath=url;
return;
}
if (!m_fullPathShown)
{
_captionPath=QFileInfo(url).fileName();
return;
}
_captionPath=QDir(QFileInfo(m_project->path()).absolutePath()).relativeFilePath(url);
if (_captionPath.contains(QLatin1String("../..")))
_captionPath=url;
else if (_captionPath.startsWith(QLatin1String("./")))
_captionPath=_captionPath.mid(2);
}
bool EditorTab::fileOpen(QString filePath, QString suggestedDirPath, bool silent)
{
if (!m_catalog->isClean())
{
switch (KMessageBox::warningYesNoCancel(SettingsController::instance()->mainWindowPtr(),
i18nc("@info","The document contains unsaved changes.\n"
"Do you want to save your changes or discard them?"),i18nc("@title:window","Warning"),
KStandardGuiItem::save(),KStandardGuiItem::discard())
)
{
case KMessageBox::Yes: if (!saveFile()) return false; break;
case KMessageBox::Cancel: return false;
default:;
}
}
if (suggestedDirPath.isEmpty())
suggestedDirPath=m_catalog->url();
QString saidPath;
if (filePath.isEmpty())
{
//Prevent crashes
//Project::instance()->model()->weaver()->suspend();
//KDE5PORT use mutex if the crash is still there with kfilemetadata library
filePath=QFileDialog::getOpenFileName(SettingsController::instance()->mainWindowPtr(), i18nc("@title:window", "Select translation file"),
suggestedDirPath, Catalog::supportedFileTypes(true));//" text/x-gettext-translation-template");
//Project::instance()->model()->weaver()->resume();
//TODO application/x-xliff, windows: just extensions
//originalPath=url.path(); never used
}
else if (!QFile::exists(filePath)&&Project::instance()->isLoaded())
{ //check if we are opening template
QString newPath=filePath;
newPath.replace(Project::instance()->poDir(),Project::instance()->potDir());
if (QFile::exists(newPath) || QFile::exists(newPath+='t'))
{
saidPath=filePath;
filePath=newPath;
}
}
if (filePath.isEmpty())
return false;
QApplication::setOverrideCursor(Qt::WaitCursor);
QString prevFilePath=currentFilePath();
bool wasOpen=!m_catalog->isEmpty();
if (wasOpen) emit fileAboutToBeClosed();
int errorLine=m_catalog->loadFromUrl(filePath,saidPath);
if (wasOpen&&errorLine==0) {emit fileClosed();emit fileClosed(prevFilePath);}
QApplication::restoreOverrideCursor();
if (errorLine==0)
{
statusBarItems.insert(ID_STATUS_TOTAL,i18nc("@info:status message entries","Total: %1", m_catalog->numberOfEntries()));
numberOfUntranslatedChanged();
numberOfFuzziesChanged();
m_currentPos.entry=-1;//so the signals are emitted
DocPosition pos(0);
//we delay gotoEntry(pos) until project is loaded;
//Project
if (!m_project->isLoaded())
{
QFileInfo fileInfo(filePath);
#ifndef NOKDE
//search for it
int i=4;
QDir dir=fileInfo.dir();
QStringList proj(QStringLiteral("*.lokalize"));
dir.setNameFilters(proj);
while (--i && !dir.isRoot() && !m_project->isLoaded())
{
if (dir.entryList().isEmpty()) {if (!dir.cdUp()) break;}
else m_project->load(dir.absoluteFilePath(dir.entryList().first()));
}
#endif
//enforce autosync
m_syncViewSecondary->mergeOpen(filePath);
if (!m_project->isLoaded())
{
if (m_project->desirablePath().isEmpty())
m_project->setDesirablePath(fileInfo.absolutePath()+QStringLiteral("/index.lokalize"));
if (m_catalog->targetLangCode().isEmpty() /*&& m_project->targetLangCode().length()*/)
m_catalog->setTargetLangCode(getTargetLangCode(fileInfo.fileName()));
//_project->setLangCode(m_catalog->targetLangCode());
}
}
if (m_catalog->targetLangCode().isEmpty() /*&& m_project->targetLangCode().length()*/)
m_catalog->setTargetLangCode(Project::instance()->targetLangCode());
gotoEntry(pos);
updateCaptionPath();
setModificationSign();
//OK!!!
emit xliffFileOpened(m_catalog->type()==Xliff);
emit fileOpened();
return true;
}
if (!silent)
{
#ifndef NOKDE
if (errorLine>0) KMessageBox::error(this, i18nc("@info","Error opening the file %1, line: %2",filePath,errorLine) );
else KMessageBox::error(this, i18nc("@info","Error opening the file %1",filePath) );
#else
KMessageBox::error(this, i18nc("@info","Error opening the file") );
#endif
}
return false;
}
bool EditorTab::saveFileAs()
{
QString filePath=QFileDialog::getSaveFileName(this, i18nc("@title:window", "Save File As"),
QFileInfo(m_catalog->url()).absoluteFilePath(), m_catalog->fileType());
if (filePath.isEmpty()) return false;
if (!Catalog::extIsSupported(filePath)&&m_catalog->url().contains('.'))
filePath+=m_catalog->url().midRef(m_catalog->url().lastIndexOf('.'));
return saveFile(filePath);
}
bool EditorTab::saveFile(const QString& filePath)
{
bool clean=m_catalog->isClean() && !m_syncView->isModified() && !m_syncViewSecondary->isModified();
if (clean) return true;
if (m_catalog->isClean() && filePath.isEmpty())
{
emit m_catalog->signalFileSaved();
return true;
}
if (m_catalog->saveToUrl(filePath))
{
updateCaptionPath();
setModificationSign();
emit fileSaved(filePath);
return true;
}
#ifndef NOKDE
if ( KMessageBox::Continue==KMessageBox::warningContinueCancel(this,
i18nc("@info","Error saving the file %1\n"
"Do you want to save to another file or cancel?", m_catalog->url()),
i18nc("@title","Error"),KStandardGuiItem::save())
)
#else
if ( QMessageBox::Yes==QMessageBox::warning(this, QString(),
i18nc("@info","Error saving the file %1\n"
"Do you want to save to another file or cancel?").arg(m_catalog->url()),
QMessageBox::Yes|QMessageBox::No)
)
#endif
return saveFileAs();
return false;
}
void EditorTab::fileAutoSaveFailedWarning(const QString& fileName)
{
KMessageBox::information(this, i18nc("@info","Could not perform file autosaving.\n"
"The target file was %1.", fileName) );
}
EditorState EditorTab::state()
{
EditorState state;
state.dockWidgets=saveState();
state.filePath=m_catalog->url();
state.mergeFilePath=m_syncView->filePath();
state.entry=m_currentPos.entry;
//state.offset=_currentPos.offset;
return state;
}
bool EditorTab::queryClose()
{
bool clean=m_catalog->isClean() && !m_syncView->isModified() && !m_syncViewSecondary->isModified();
if (clean) return true;
//TODO caption
switch (KMessageBox::warningYesNoCancel(this,
i18nc("@info","The document contains unsaved changes.\n"
"Do you want to save your changes or discard them?"),i18nc("@title:window","Warning"),
KStandardGuiItem::save(),KStandardGuiItem::discard()))
{
case KMessageBox::Yes: return saveFile();
case KMessageBox::No: return true;
default: return false;
}
}
void EditorTab::undo()
{
gotoEntry(m_catalog->undo(),0);
msgStrChanged();
}
void EditorTab::redo()
{
gotoEntry(m_catalog->redo(),0);
msgStrChanged();
}
void EditorTab::gotoEntry()
{
bool ok=false;
DocPosition pos=m_currentPos;
pos.entry=QInputDialog::getInt(this, i18nc("@title","Jump to Entry"),
i18nc("@label:spinbox","Enter entry number:"),
pos.entry,1,m_catalog->numberOfEntries(),1, &ok);
if (ok)
{
--(pos.entry);
gotoEntry(pos);
}
}
void EditorTab::gotoEntry(DocPosition pos, int selection)
{
//specially for dbus users
if (pos.entry>=m_catalog->numberOfEntries()||pos.entry<0)
return;
if (!m_catalog->isPlural(pos))
pos.form=0;
m_currentPos.part=pos.part;//for searching;
//UndefPart => called on fuzzy toggle
bool newEntry=m_currentPos.entry!=pos.entry || m_currentPos.form!=pos.form;
if (newEntry||pos.part==DocPosition::Comment)
{
m_notesView->gotoEntry(pos,pos.part==DocPosition::Comment?selection:0);
if (pos.part==DocPosition::Comment)
{
pos.form=0;
pos.offset=0;
selection=0;
}
}
m_view->gotoEntry(pos,selection);
if (pos.part==DocPosition::UndefPart)
m_currentPos.part=DocPosition::Target;
//QTime time; time.start();
if (newEntry)
{
m_currentPos=pos;
if (true)
{
emit signalNewEntryDisplayed(pos);
emit entryDisplayed();
emit signalFirstDisplayed(pos.entry==m_transUnitsView->firstEntryNumber());
emit signalLastDisplayed(pos.entry==m_transUnitsView->lastEntryNumber());
emit signalPriorFuzzyAvailable(pos.entry>m_catalog->firstFuzzyIndex());
emit signalNextFuzzyAvailable(pos.entrylastFuzzyIndex());
emit signalPriorUntranslatedAvailable(pos.entry>m_catalog->firstUntranslatedIndex());
emit signalNextUntranslatedAvailable(pos.entrylastUntranslatedIndex());
emit signalPriorFuzzyOrUntrAvailable(pos.entry>m_catalog->firstFuzzyIndex()
||pos.entry>m_catalog->firstUntranslatedIndex()
);
emit signalNextFuzzyOrUntrAvailable(pos.entrylastFuzzyIndex()
||pos.entrylastUntranslatedIndex());
emit signalPriorBookmarkAvailable(pos.entry>m_catalog->firstBookmarkIndex());
emit signalNextBookmarkAvailable(pos.entrylastBookmarkIndex());
emit signalBookmarkDisplayed(m_catalog->isBookmarked(pos.entry));
emit signalEquivTranslatedEntryDisplayed(m_catalog->isEquivTrans(pos));
emit signalApprovedEntryDisplayed(m_catalog->isApproved(pos));
}
}
statusBarItems.insert(ID_STATUS_CURRENT,i18nc("@info:status","Current: %1", m_currentPos.entry+1));
//qCDebug(LOKALIZE_LOG)<<"ELA "<msgstr(m_currentPos).isEmpty();
bool isApproved=m_catalog->isApproved(m_currentPos);
if (isUntr==m_currentIsUntr && isApproved==m_currentIsApproved)
return;
QString msg;
if (isUntr) msg=i18nc("@info:status","Untranslated");
else if (isApproved)msg=i18nc("@info:status 'non-fuzzy' in gettext terminology","Ready");
else msg=i18nc("@info:status 'fuzzy' in gettext terminology","Needs review");
/* else
statusBar()->changeItem("",ID_STATUS_ISFUZZY);*/
statusBarItems.insert(ID_STATUS_ISFUZZY,msg);
m_currentIsUntr=isUntr;
m_currentIsApproved=isApproved;
}
void EditorTab::switchForm(int newForm)
{
if (m_currentPos.form==newForm) return;
DocPosition pos=m_currentPos;
pos.form=newForm;
gotoEntry(pos);
}
void EditorTab::gotoNextUnfiltered()
{
DocPosition pos=m_currentPos;
if (switchNext(m_catalog,pos))
gotoEntry(pos);
}
void EditorTab::gotoPrevUnfiltered()
{
DocPosition pos=m_currentPos;
if (switchPrev(m_catalog,pos))
gotoEntry(pos);
}
void EditorTab::gotoFirstUnfiltered(){gotoEntry(DocPosition(0));}
void EditorTab::gotoLastUnfiltered(){gotoEntry(DocPosition(m_catalog->numberOfEntries()-1));}
void EditorTab::gotoFirst()
{
DocPosition pos=DocPosition(m_transUnitsView->firstEntryNumber());
if (pos.entry!=-1)
gotoEntry(pos);
}
void EditorTab::gotoLast()
{
DocPosition pos=DocPosition(m_transUnitsView->lastEntryNumber());
if (pos.entry!=-1)
gotoEntry(pos);
}
void EditorTab::gotoNext()
{
DocPosition pos=m_currentPos;
if (m_catalog->isPlural(pos) && pos.form+1numberOfPluralForms())
pos.form++;
else
pos=DocPosition(m_transUnitsView->nextEntryNumber());
if (pos.entry!=-1)
gotoEntry(pos);
}
void EditorTab::gotoPrev()
{
DocPosition pos=m_currentPos;
if (m_catalog->isPlural(pos) && pos.form>0)
pos.form--;
else
pos=DocPosition(m_transUnitsView->prevEntryNumber());
if (pos.entry!=-1)
gotoEntry(pos);
}
void EditorTab::gotoPrevFuzzy()
{
DocPosition pos;
if ( (pos.entry=m_catalog->prevFuzzyIndex(m_currentPos.entry)) == -1)
return;
gotoEntry(pos);
}
void EditorTab::gotoNextFuzzy()
{
DocPosition pos;
if ( (pos.entry=m_catalog->nextFuzzyIndex(m_currentPos.entry)) == -1)
return;
gotoEntry(pos);
}
void EditorTab::gotoPrevUntranslated()
{
DocPosition pos;
if ( (pos.entry=m_catalog->prevUntranslatedIndex(m_currentPos.entry)) == -1)
return;
gotoEntry(pos);
}
void EditorTab::gotoNextUntranslated()
{
DocPosition pos;
if ( (pos.entry=m_catalog->nextUntranslatedIndex(m_currentPos.entry)) == -1)
return;
gotoEntry(pos);
}
void EditorTab::gotoPrevFuzzyUntr()
{
DocPosition pos;
short fu = m_catalog->prevFuzzyIndex(m_currentPos.entry);
short un = m_catalog->prevUntranslatedIndex(m_currentPos.entry);
pos.entry=fu>un?fu:un;
if ( pos.entry == -1)
return;
gotoEntry(pos);
}
bool EditorTab::gotoNextFuzzyUntr(const DocPosition& p)
{
int index=(p.entry==-1)?m_currentPos.entry:p.entry;
DocPosition pos;
short fu = m_catalog->nextFuzzyIndex(index);
short un = m_catalog->nextUntranslatedIndex(index);
if ( (fu == -1) && (un == -1) )
return false;
if (fu == -1) fu=un;
else if (un == -1) un=fu;
pos.entry=fuisApproved(m_currentPos.entry))
m_view->toggleApprovement();
if (!gotoNextFuzzyUntr())
gotoNextFuzzyUntr(DocPosition(-2));//so that we don't skip the first
}
void EditorTab::setApproveActionTitle()
{
const char* const titles[]={
I18N_NOOP2("@option:check trans-unit state","Translated"),
I18N_NOOP2("@option:check trans-unit state","Signed-off"),
I18N_NOOP2("@option:check trans-unit state","Approved")
};
const char* const helpText[]={
I18N_NOOP2("@info:tooltip","Translation is done (although still may need a review)"),
I18N_NOOP2("@info:tooltip","Translation has received positive review"),
I18N_NOOP2("@info:tooltip","Entry is fully localized (i.e. final)")
};
int role=m_catalog->activePhaseRole();
if (role==ProjectLocal::Undefined)
role=Project::local()->role();
m_approveAction->setText(i18nc("@option:check trans-unit state",titles[role]));
m_approveAction->setToolTip(i18nc("@info:tooltip",helpText[role]));
m_approveAndGoAction->setVisible(role==ProjectLocal::Approver);
#ifdef NOKDE
m_stateAction->setVisible(m_catalog->capabilities()&ExtendedStates);
#endif
}
void EditorTab::showStatesMenu()
{
m_stateAction->menu()->clear();
if (!(m_catalog->capabilities()&ExtendedStates))
{
QAction* a=m_stateAction->menu()->addAction(i18nc("@info:status 'fuzzy' in gettext terminology","Needs review"));
a->setData(QVariant(-1));
a->setCheckable(true);
a->setChecked(!m_catalog->isApproved(m_currentPos));
a=m_stateAction->menu()->addAction(i18nc("@info:status 'non-fuzzy' in gettext terminology","Ready"));
a->setData(QVariant(-2));
a->setCheckable(true);
a->setChecked(m_catalog->isApproved(m_currentPos));
return;
}
TargetState state=m_catalog->state(m_currentPos);
const char* const* states=Catalog::states();
for (int i=0;imenu()->addAction(i18n(states[i]));
a->setData(QVariant(i));
a->setCheckable(true);
a->setChecked(state==i);
if (i==New || i==Translated || i==Final)
m_stateAction->menu()->addSeparator();
}
}
void EditorTab::setState(QAction* a)
{
if (!(m_catalog->capabilities()&ExtendedStates))
m_view->toggleApprovement();
else
m_view->setState(TargetState(a->data().toInt()));
m_stateAction->menu()->clear();
}
void EditorTab::openPhasesWindow()
{
PhasesWindow w(m_catalog, this);
w.exec();
}
void EditorTab::gotoPrevBookmark()
{
DocPosition pos;
if ( (pos.entry=m_catalog->prevBookmarkIndex(m_currentPos.entry)) == -1)
return;
gotoEntry(pos);
}
void EditorTab::gotoNextBookmark()
{
DocPosition pos;
if ( (pos.entry=m_catalog->nextBookmarkIndex(m_currentPos.entry)) == -1)
return;
gotoEntry(pos);
}
//wrapper for cmdline handling...
void EditorTab::mergeOpen(QString mergeFilePath)
{
m_syncView->mergeOpen(mergeFilePath);
}
//HACK to prevent redundant repaintings when widget isn't visible
void EditorTab::paintEvent(QPaintEvent* event)
{
if (QMdiSubWindow* sw=qobject_cast(parent()))
{
if (sw->mdiArea()->currentSubWindow()!=sw)
return;
}
LokalizeSubwindowBase2::paintEvent(event);
}
void EditorTab::indexWordsForCompletion()
{
CompletionStorage::instance()->scanCatalog(m_catalog);
}
void EditorTab::displayWordCount()
{
//TODO in trans and fuzzy separately
int sourceCount=0;
int targetCount=0;
QRegExp rxClean(Project::instance()->markup()%'|'%Project::instance()->accel());//cleaning regexp; NOTE isEmpty()?
QRegExp rxSplit(QStringLiteral("\\W|\\d"));//splitting regexp
DocPosition pos(0);
do
{
QString msg=m_catalog->source(pos);
msg.remove(rxClean);
QStringList words=msg.split(rxSplit,QString::SkipEmptyParts);
sourceCount+=words.size();
msg=m_catalog->target(pos);
msg.remove(rxClean);
words=msg.split(rxSplit,QString::SkipEmptyParts);
targetCount+=words.size();
}
while (switchNext(m_catalog,pos));
KMessageBox::information(this, i18nc("@info words count",
"Source text words: %1 Target text words: %2",
sourceCount,targetCount),i18nc("@title","Word Count"));
}
bool EditorTab::findEntryBySourceContext(const QString& source, const QString& ctxt)
{
DocPosition pos(0);
do
{
if (m_catalog->source(pos)==source && m_catalog->context(pos.entry)==QStringList(ctxt))
{
gotoEntry(pos);
return true;
}
}
while (switchNext(m_catalog,pos));
return false;
}
//see also termlabel.h
void EditorTab::defineNewTerm()
{
//TODO just a word under cursor?
QString en(m_view->selectionInSource().toLower());
if (en.isEmpty())
en=m_catalog->msgid(m_currentPos).toLower();
QString target(m_view->selectionInTarget().toLower());
if (target.isEmpty())
target=m_catalog->msgstr(m_currentPos).toLower();
m_project->defineNewTerm(en,target);
}
void EditorTab::reloadFile()
{
QString mergeFilePath=m_syncView->filePath();
DocPosition p=m_currentPos;
if (!fileOpen(currentFilePath()))
return;
gotoEntry(p);
if (!mergeFilePath.isEmpty())
mergeOpen(mergeFilePath);
}
static void openLxrSearch(const QString& srcFileRelPath)
{
QDesktopServices::openUrl(QUrl(QStringLiteral("https://lxr.kde.org/search?_filestring=")
+ QString::fromLatin1(QUrl::toPercentEncoding(srcFileRelPath))));
}
void EditorTab::dispatchSrcFileOpenRequest(const QString& srcFileRelPath, int line)
{
// Try project scripts first.
m_srcFileOpenRequestAccepted = false;
emit srcFileOpenRequested(srcFileRelPath, line);
if (m_srcFileOpenRequestAccepted)
return;
// If project scripts do not handle opening the source file, check if the
// path exists relative to the current translation file path.
QDir relativePath(currentFilePath());
relativePath.cdUp();
QString srcAbsolutePath(relativePath.absoluteFilePath(srcFileRelPath));
if (QFile::exists(srcAbsolutePath)) {
QDesktopServices::openUrl(QUrl::fromLocalFile(srcAbsolutePath));
return;
}
QString dir = Project::instance()->local()->sourceDir();
if (dir.isEmpty())
{
switch (KMessageBox::questionYesNoCancel(SettingsController::instance()->mainWindowPtr(),
i18nc("@info","Would you like to search for the source file locally or via lxr.kde.org?"),i18nc("@title:window","Source file lookup"),
KGuiItem(i18n("Locally")), KGuiItem("lxr.kde.org")
))
{
case KMessageBox::Yes: break;
case KMessageBox::No: openLxrSearch(srcFileRelPath);
case KMessageBox::Cancel:
default:
return;
}
}
//hard fallback
if (dir.isEmpty())
{
dir = QFileDialog::getExistingDirectory(this, i18n("Select project's base folder for source file lookup"), QDir::homePath());
if (dir.length())
Project::instance()->local()->setSourceDir(dir);
}
if (dir.length())
{
auto doOpen = [srcFileRelPath]()
{
auto sourceFilePaths = Project::instance()->sourceFilePaths();
bool found = false;
QByteArray fn = srcFileRelPath.midRef(srcFileRelPath.lastIndexOf('/')+1).toUtf8();
auto it=sourceFilePaths.constFind(fn);
while (it!=sourceFilePaths.constEnd() && it.key() == fn)
{
const QString absFilePath = QString::fromUtf8(it.value()+'/'+fn);
if (!absFilePath.endsWith(srcFileRelPath) || !QFileInfo::exists(absFilePath) ) //for the case when link contained also folders
{
it++;
continue;
}
found = true;
QDesktopServices::openUrl(QUrl::fromLocalFile(absFilePath));
it++;
}
if (!found)
{
switch (KMessageBox::warningYesNoCancel(SettingsController::instance()->mainWindowPtr(),
i18nc("@info","Could not find source file in the folder specified.\n"
"Do you want to change source files folder?"),i18nc("@title:window","Source file lookup"),
KStandardGuiItem::yes(), KStandardGuiItem::no(), KGuiItem(i18n("lxr.kde.org"))))
{
case KMessageBox::Cancel:
Project::instance()->local()->setSourceDir(QString());
Project::instance()->resetSourceFilePaths();
openLxrSearch(srcFileRelPath);
case KMessageBox::No:
return;
default: ; //fall through to dir selection
}
QString dir = QFileDialog::getExistingDirectory(0, i18n("Select project's base folder for source file lookup"), Project::instance()->local()->sourceDir());
if (dir.length())
{
Project::instance()->local()->setSourceDir(dir);
Project::instance()->resetSourceFilePaths();
}
}
};
if (!Project::instance()->sourceFilePaths().isEmpty())
doOpen();
else
connect(Project::instance(), &Project::sourceFilePathsAreReady, doOpen);
return;
}
// Otherwise, let the user know how to create a project script to handle
// opening source file paths that are not relative to translation files.
KMessageBox::information(this, i18nc("@info",
"Cannot open the target source file: The target source file is not "
"relative to the current translation file, and there are currently no "
"scripts loaded to handle opening source files in custom paths. Refer "
"to the Lokalize handbook for script examples and how to plug them "
"into your project."));
}
void EditorTab::mergeIntoOpenDocument()
{
if (!m_catalog || m_catalog->type()!=Xliff)
return;
QString xliff2odf=QStringLiteral("xliff2odf");
if (QProcess::execute(xliff2odf, QStringList(QLatin1String("--version")))==-2)
{
KMessageBox::error(SettingsController::instance()->mainWindowPtr(),
i18n("Install translate-toolkit package and retry."));
return;
}
QString xliffFolder=QFileInfo(m_catalog->url()).absolutePath();
QString originalOdfFilePath=m_catalog->originalOdfFilePath();
if (originalOdfFilePath.isEmpty() || !QFile::exists(originalOdfFilePath))
{
originalOdfFilePath=QFileDialog::getOpenFileName(
SettingsController::instance()->mainWindowPtr(),
i18n("Select original OpenDocument on which current XLIFF file is based"),
xliffFolder,
i18n("OpenDocument files (*.odt *.ods)")/*"text/x-lokalize-project"*/);
if (originalOdfFilePath.length()) m_catalog->setOriginalOdfFilePath(originalOdfFilePath);
}
if (originalOdfFilePath.isEmpty())
return;
saveFile();
//TODO check if odt did update (merge with new template is needed)
QFileInfo originalOdfFileInfo(originalOdfFilePath);
QString targetLangCode=m_catalog->targetLangCode();
QStringList args(m_catalog->url());
args.append(xliffFolder%'/'%originalOdfFileInfo.baseName()%'-'%targetLangCode%'.'%originalOdfFileInfo.suffix());
args.append(QStringLiteral("-t"));
args.append(originalOdfFilePath);
qCDebug(LOKALIZE_LOG)<mainWindowPtr(), i18n("Install translate-toolkit package and retry"));
return;
}
QProcess::startDetached(lowriter, QStringList(args.at(1)));
QString reloaderScript=QStandardPaths::locate(QStandardPaths::DataLocation, QStringLiteral("scripts/odf/xliff2odf-standalone.py"));
if (reloaderScript.length())
{
QString python=QStringLiteral("python");
QStringList unoArgs(QStringLiteral("-c")); unoArgs.append(QStringLiteral("import uno"));
if (QProcess::execute(python, unoArgs)!=0)
{
python=QStringLiteral("python3");
QStringList unoArgs(QStringLiteral("-c")); unoArgs.append(QStringLiteral("import uno"));
if (QProcess::execute(python, unoArgs)!=0)
{
KMessageBox::information(SettingsController::instance()->mainWindowPtr(),
i18n("Install python-uno package for additional functionality."));
return;
}
}
QStringList reloaderArgs(reloaderScript);
reloaderArgs.append(args.at(1));
reloaderArgs.append(currentEntryId());
QProcess::execute(python, reloaderArgs);
}
}
}
//BEGIN DBus interface
#ifndef NOKDE
#include "editoradaptor.h"
QList EditorTab::ids;
QString EditorTab::dbusObjectPath()
{
const QString EDITOR_PATH=QStringLiteral("/ThisIsWhatYouWant/Editor/");
if ( m_dbusId==-1 )
{
m_adaptor=new EditorAdaptor(this);
int i=0;
while(iurl();}
QByteArray EditorTab::currentFileContents(){return m_catalog->contents();}
QString EditorTab::currentEntryId(){return m_catalog->id(m_currentPos);}
QString EditorTab::selectionInTarget(){return m_view->selectionInTarget();}
QString EditorTab::selectionInSource(){return m_view->selectionInSource();}
void EditorTab::lookupSelectionInTranslationMemory(){emit tmLookupRequested(selectionInSource(),selectionInTarget());}
void EditorTab::setEntryFilteredOut(int entry, bool filteredOut){m_transUnitsView->setEntryFilteredOut(entry, filteredOut);}
void EditorTab::setEntriesFilteredOut(bool filteredOut){m_transUnitsView->setEntriesFilteredOut(filteredOut);}
int EditorTab::entryCount(){return m_catalog->numberOfEntries();}
QString EditorTab::entrySource(int entry, int form){return m_catalog->sourceWithTags(DocPosition(entry, form)).string;}
QString EditorTab::entryTarget(int entry, int form){return m_catalog->targetWithTags(DocPosition(entry, form)).string;}
int EditorTab::entryPluralFormCount(int entry){return m_catalog->isPlural(entry)?m_catalog->numberOfPluralForms():1;}
bool EditorTab::entryReady(int entry){return m_catalog->isApproved(entry);}
QString EditorTab::sourceLangCode(){return m_catalog->sourceLangCode();}
QString EditorTab::targetLangCode(){return m_catalog->targetLangCode();}
void EditorTab::addEntryNote(int entry, const QString& note){m_notesView->addNote(entry, note);}
void EditorTab::addTemporaryEntryNote(int entry, const QString& note){m_notesView->addTemporaryEntryNote(entry, note);}
void EditorTab::addAlternateTranslation(int entry, const QString& translation){m_altTransView->addAlternateTranslation(entry, translation);}
void EditorTab::addTemporaryAlternateTranslation(int entry, const QString& translation){m_altTransView->addAlternateTranslation(entry, translation);}
void EditorTab::attachAlternateTranslationFile(const QString& path){m_altTransView->attachAltTransFile(path);}
void EditorTab::setEntryTarget(int entry, int form, const QString& content)
{
DocPosition pos(entry,form);
m_catalog->beginMacro(i18nc("@item Undo action item","Set unit text"));
removeTargetSubstring(m_catalog, pos);
insertCatalogString(m_catalog, pos, CatalogString(content));
m_catalog->endMacro();
if (m_currentPos==pos)
m_view->gotoEntry();
}
//END DBus interface
diff --git a/src/editortab_findreplace.cpp b/src/editortab_findreplace.cpp
index 062fbc9..c7e4d20 100644
--- a/src/editortab_findreplace.cpp
+++ b/src/editortab_findreplace.cpp
@@ -1,664 +1,663 @@
/* ****************************************************************************
This file is part of Lokalize
Copyright (C) 2007-2009 by Nick Shaforostoff
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) version 3 or any later version
accepted by the membership of KDE e.V. (or its successor approved
by the membership of KDE e.V.), which shall act as a proxy
defined in Section 14 of version 3 of the license.
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, see .
**************************************************************************** */
#include "lokalize_debug.h"
#include "editortab.h"
#include "editorview.h"
#include "catalog.h"
#include "pos.h"
#include "cmd.h"
#include "project.h"
#include "prefs_lokalize.h"
#include "ui_kaider_findextension.h"
#include "stemming.h"
#include
#include
#include
#include
-#include
#include
#include
#include
#include
#define IGNOREACCELS KFind::MinimumUserOption
#define INCLUDENOTES KFind::MinimumUserOption*2
static long makeOptions(long options, const Ui_findExtension* ui_findExtension)
{
return options
+IGNOREACCELS*ui_findExtension->m_ignoreAccelMarks->isChecked()
+INCLUDENOTES*ui_findExtension->m_notes->isChecked();
//bool skipMarkup(){return ui_findExtension->m_skipTags->isChecked();}
}
class EntryFindDialog: public KFindDialog
{
public:
EntryFindDialog(QWidget* parent);
~EntryFindDialog();
long options() const{return makeOptions(KFindDialog::options(),ui_findExtension);}
static EntryFindDialog* instance(QWidget* parent=0);
private:
static QPointer _instance;
static void cleanup(){delete EntryFindDialog::_instance;}
private:
Ui_findExtension* ui_findExtension;
};
QPointer EntryFindDialog::_instance=0;
EntryFindDialog* EntryFindDialog::instance(QWidget* parent)
{
if (_instance==0 )
{
_instance=new EntryFindDialog(parent);
qAddPostRoutine(EntryFindDialog::cleanup);
}
return _instance;
}
EntryFindDialog::EntryFindDialog(QWidget* parent)
: KFindDialog(parent)
, ui_findExtension(new Ui_findExtension)
{
ui_findExtension->setupUi(findExtension());
setHasSelection(false);
KConfig config;
KConfigGroup stateGroup(&config,"FindReplace");
setOptions(stateGroup.readEntry("FindOptions",(qlonglong)0));
setFindHistory(stateGroup.readEntry("FindHistory",QStringList()));
}
EntryFindDialog::~EntryFindDialog()
{
KConfig config;
KConfigGroup stateGroup(&config,"FindReplace");
stateGroup.writeEntry("FindOptions",(qlonglong)options());
stateGroup.writeEntry("FindHistory",findHistory());
delete ui_findExtension;
}
//BEGIN EntryReplaceDialog
class EntryReplaceDialog: public KReplaceDialog
{
public:
EntryReplaceDialog(QWidget* parent);
~EntryReplaceDialog();
long options() const{return makeOptions(KReplaceDialog::options(),ui_findExtension);}
static EntryReplaceDialog* instance(QWidget* parent=0);
private:
static QPointer _instance;
static void cleanup(){delete EntryReplaceDialog::_instance;}
private:
Ui_findExtension* ui_findExtension;
};
QPointer EntryReplaceDialog::_instance=0;
EntryReplaceDialog* EntryReplaceDialog::instance(QWidget* parent)
{
if (_instance==0 )
{
_instance=new EntryReplaceDialog(parent);
qAddPostRoutine(EntryReplaceDialog::cleanup);
}
return _instance;
}
EntryReplaceDialog::EntryReplaceDialog(QWidget* parent)
: KReplaceDialog(parent)
, ui_findExtension(new Ui_findExtension)
{
ui_findExtension->setupUi(findExtension());
//ui_findExtension->m_notes->hide();
setHasSelection(false);
KConfig config;
KConfigGroup stateGroup(&config,"FindReplace");
setOptions(stateGroup.readEntry("ReplaceOptions",(qlonglong)0));
setFindHistory(stateGroup.readEntry("ReplacePatternHistory",QStringList()));
setReplacementHistory(stateGroup.readEntry("ReplacementHistory",QStringList()));
}
EntryReplaceDialog::~EntryReplaceDialog()
{
KConfig config;
KConfigGroup stateGroup(&config,"FindReplace");
stateGroup.writeEntry("ReplaceOptions",(qlonglong)options());
stateGroup.writeEntry("ReplacePatternHistory",findHistory());
stateGroup.writeEntry("ReplacementHistory",replacementHistory());
delete ui_findExtension;
}
//END EntryReplaceDialog
//TODO &,
static void calcOffsetWithAccels(const QString& data, int& offset, int& length)
{
int i=0;
for (;ioptions() & KFind::FindBackwards)
{
pos.entry=m_catalog->numberOfEntries()-1;
pos.form=(m_catalog->isPlural(pos.entry))?
m_catalog->numberOfPluralForms()-1:0;
}
else
{
pos.entry=0;
pos.form=0;
}
return true;
}
void EditorTab::find()
{
//QWidget* p=0; QWidget* next=qobject_cast(parent()); while(next) { p=next; next=qobject_cast(next->parent()); }
EntryFindDialog::instance(nativeParentWidget());
QString sel=selectionInTarget();
if (!(sel.isEmpty()&&selectionInSource().isEmpty()))
{
if (sel.isEmpty())
sel=selectionInSource();
if (m_find&&m_find->options()&IGNOREACCELS)
sel.remove('&');
EntryFindDialog::instance()->setPattern(sel);
}
if ( EntryFindDialog::instance()->exec() != QDialog::Accepted )
return;
if (m_find)
{
m_find->resetCounts();
m_find->setPattern(EntryFindDialog::instance()->pattern());
m_find->setOptions(EntryFindDialog::instance()->options());
}
else // This creates a find-next-prompt dialog if needed.
{
m_find = new KFind(EntryFindDialog::instance()->pattern(),EntryFindDialog::instance()->options(),this,EntryFindDialog::instance());
connect(m_find,SIGNAL(highlight(QString,int,int)),this, SLOT(highlightFound(QString,int,int)) );
connect(m_find,SIGNAL(findNext()),this,SLOT(findNext()));
m_find->closeFindNextDialog();
}
DocPosition pos;
if (m_find->options() & KFind::FromCursor)
pos=m_currentPos;
else if (!determineStartingPos(m_catalog, m_find, pos))
return;
findNext(pos);
}
void EditorTab::findNext(const DocPosition& startingPos)
{
Catalog& catalog=*m_catalog;
KFind& find=*m_find;
- if (KDE_ISUNLIKELY( catalog.numberOfEntries()<=startingPos.entry ))
+ if (Q_UNLIKELY( catalog.numberOfEntries()<=startingPos.entry ))
return;//for the case when app wasn't able to process event before file close
bool anotherEntry=_searchingPos.entry!=m_currentPos.entry;
_searchingPos=startingPos;
if (anotherEntry)
_searchingPos.offset=0;
QRegExp rx("[^(\\\\n)>]\n");
QTime a;a.start();
//_searchingPos.part=DocPosition::Source;
bool ignoreaccels=m_find->options()&IGNOREACCELS;
bool includenotes=m_find->options()&INCLUDENOTES;
int switchOptions=DocPosition::Source|DocPosition::Target|(includenotes*DocPosition::Comment);
int flag=1;
while (flag)
{
flag=0;
KFind::Result res = KFind::NoMatch;
while (true)
{
if (find.needData()||anotherEntry||m_view->m_modifiedAfterFind)
{
anotherEntry=false;
m_view->m_modifiedAfterFind=false;
QString data;
if (_searchingPos.part==DocPosition::Comment)
data=catalog.notes(_searchingPos).at(_searchingPos.form).content;
else
data=catalog.catalogString(_searchingPos).string;
if (ignoreaccels)
data.remove('&');
find.setData(data);
}
res = find.find();
//offset=-1;
if (res!=KFind::NoMatch)
break;
if (!(
(find.options()&KFind::FindBackwards)?
switchPrev(m_catalog,_searchingPos,switchOptions):
switchNext(m_catalog,_searchingPos,switchOptions)
))
break;
}
if (res==KFind::NoMatch)
{
//file-wide search
if(find.shouldRestart(true,true))
{
flag=1;
determineStartingPos(m_catalog, m_find,_searchingPos);
}
find.resetCounts();
}
}
}
void EditorTab::findNext()
{
if (m_find)
{
findNext((m_currentPos.entry==_searchingPos.entry&&_searchingPos.part==DocPosition::Comment)?
_searchingPos:m_currentPos);
}
else
find();
}
void EditorTab::findPrev()
{
if (m_find)
{
m_find->setOptions(m_find->options() ^ KFind::FindBackwards);
findNext(m_currentPos);
}
else
{
find();
}
}
void EditorTab::highlightFound(const QString &,int matchingIndex,int matchedLength)
{
if (m_find->options()&IGNOREACCELS && _searchingPos.part!=DocPosition::Comment)
{
QString data=m_catalog->catalogString(_searchingPos).string;
calcOffsetWithAccels(data, matchingIndex, matchedLength);
}
_searchingPos.offset=matchingIndex;
gotoEntry(_searchingPos,matchedLength);
}
void EditorTab::replace()
{
EntryReplaceDialog::instance(nativeParentWidget());
if (!m_view->selectionInTarget().isEmpty())
{
if (m_replace&&m_replace->options()&IGNOREACCELS)
{
QString tmp(m_view->selectionInTarget());
tmp.remove('&');
EntryReplaceDialog::instance()->setPattern(tmp);
}
else
EntryReplaceDialog::instance()->setPattern(m_view->selectionInTarget());
}
if ( EntryReplaceDialog::instance()->exec() != QDialog::Accepted )
return;
if (m_replace) m_replace->deleteLater();// _replace=0;
// This creates a find-next-prompt dialog if needed.
{
m_replace = new KReplace(EntryReplaceDialog::instance()->pattern(),EntryReplaceDialog::instance()->replacement(),EntryReplaceDialog::instance()->options(),this,EntryReplaceDialog::instance());
connect(m_replace,SIGNAL(highlight(QString,int,int)), this,SLOT(highlightFound_(QString,int,int)));
connect(m_replace,SIGNAL(findNext()), this,SLOT(replaceNext()));
connect(m_replace,SIGNAL(replace(QString,int,int,int)), this,SLOT(doReplace(QString,int,int,int)));
connect(m_replace,SIGNAL(dialogClosed()), this,SLOT(cleanupReplace()));
// _replace->closeReplaceNextDialog();
}
// else
// {
// _replace->resetCounts();
// _replace->setPattern(EntryReplaceDialog::instance()->pattern());
// _replace->setOptions(EntryReplaceDialog::instance()->options());
// }
//m_catalog->beginMacro(i18nc("@item Undo action item","Replace"));
m_doReplaceCalled=false;
if (m_replace->options() & KFind::FromCursor)
replaceNext(m_currentPos);
else
{
DocPosition pos;
if (!determineStartingPos(m_catalog, m_replace,pos)) return;
replaceNext(pos);
}
}
void EditorTab::replaceNext(const DocPosition& startingPos)
{
bool anotherEntry=m_currentPos.entry!=_replacingPos.entry;
_replacingPos=startingPos;
if (anotherEntry)
_replacingPos.offset=0;
int flag=1;
bool ignoreaccels=m_replace->options()&IGNOREACCELS;
bool includenotes=m_replace->options()&INCLUDENOTES;
qCWarning(LOKALIZE_LOG)<<"includenotes"<needData()||anotherEntry/*||m_view->m_modifiedAfterFind*/)
{
anotherEntry=false;
//m_view->m_modifiedAfterFind=false;//NOTE TEST THIS
QString data;
if (_replacingPos.part==DocPosition::Comment)
data=m_catalog->notes(_replacingPos).at(_replacingPos.form).content;
else
{
data=m_catalog->targetWithTags(_replacingPos).string;
if (ignoreaccels) data.remove('&');
}
m_replace->setData(data);
}
res = m_replace->replace();
if (res!=KFind::NoMatch)
break;
if (!(
(m_replace->options()&KFind::FindBackwards)?
switchPrev(m_catalog,_replacingPos,switchOptions):
switchNext(m_catalog,_replacingPos,switchOptions)
))
break;
}
if (res==KFind::NoMatch)
{
if((m_replace->options()&KFind::FromCursor)
&&m_replace->shouldRestart(true))
{
flag=1;
determineStartingPos(m_catalog, m_replace,_replacingPos);
}
else
{
if(!(m_replace->options() & KFind::FromCursor))
m_replace->displayFinalDialog();
m_replace->closeReplaceNextDialog();
cleanupReplace();
}
m_replace->resetCounts();
}
}
}
void EditorTab::cleanupReplace()
{
if(m_doReplaceCalled)
{
m_doReplaceCalled=false;
m_catalog->endMacro();
}
}
void EditorTab::replaceNext()
{
replaceNext(m_currentPos);
}
void EditorTab::highlightFound_(const QString &,int matchingIndex,int matchedLength)
{
if (m_replace->options()&IGNOREACCELS)
{
QString data=m_catalog->targetWithTags(_replacingPos).string;
calcOffsetWithAccels(data,matchingIndex,matchedLength);
}
_replacingPos.offset=matchingIndex;
gotoEntry(_replacingPos,matchedLength);
}
void EditorTab::doReplace(const QString &newStr,int offset,int newLen,int remLen)
{
if(!m_doReplaceCalled)
{
m_doReplaceCalled=true;
m_catalog->beginMacro(i18nc("@item Undo action item","Replace"));
}
DocPosition pos=_replacingPos;
if (_replacingPos.part==DocPosition::Comment)
m_catalog->push(new SetNoteCmd(m_catalog,pos,newStr));
else
{
QString oldStr=m_catalog->target(_replacingPos);
if (m_replace->options()&IGNOREACCELS)
calcOffsetWithAccels(oldStr,offset,remLen);
pos.offset=offset;
m_catalog->push(new DelTextCmd(m_catalog,pos,oldStr.mid(offset,remLen)));
if (newLen)
m_catalog->push(new InsTextCmd(m_catalog,pos,newStr.mid(offset,newLen)));
}
if (pos.entry==m_currentPos.entry)
{
pos.offset+=newLen;
m_view->gotoEntry(pos);
}
}
void EditorTab::spellcheck()
{
if (!m_sonnetDialog)
{
m_sonnetChecker=new Sonnet::BackgroundChecker(this);
m_sonnetChecker->changeLanguage(enhanceLangCode(Project::instance()->langCode()));
m_sonnetDialog=new Sonnet::Dialog(m_sonnetChecker,this);
connect(m_sonnetDialog,SIGNAL(done(QString)),this,SLOT(spellcheckNext()));
connect(m_sonnetDialog,SIGNAL(replace(QString,int,QString)),
this,SLOT(spellcheckReplace(QString,int,QString)));
connect(m_sonnetDialog,SIGNAL(stop()),this,SLOT(spellcheckStop()));
connect(m_sonnetDialog,SIGNAL(cancel()),this,SLOT(spellcheckCancel()));
connect(m_sonnetDialog/*m_sonnetChecker*/,SIGNAL(misspelling(QString,int)),
this,SLOT(spellcheckShow(QString,int)));
// disconnect(/*m_sonnetDialog*/m_sonnetChecker,SIGNAL(misspelling(QString,int)),
// m_sonnetDialog,SLOT(slotMisspelling(QString,int)));
//
// connect( d->checker, SIGNAL(misspelling(const QString&, int)),
// SLOT(slotMisspelling(const QString&, int)) );
}
QString text=m_catalog->msgstr(m_currentPos);
if (!m_view->selectionInTarget().isEmpty())
text=m_view->selectionInTarget();
text.remove('&');
m_sonnetDialog->setBuffer(text);
_spellcheckPos=m_currentPos;
_spellcheckStartPos=m_currentPos;
m_spellcheckStop=false;
//m_catalog->beginMacro(i18n("Spellcheck"));
m_spellcheckStartUndoIndex=m_catalog->index();
m_sonnetDialog->show();
}
void EditorTab::spellcheckNext()
{
if (m_spellcheckStop)
return;
do
{
if (!switchNext(m_catalog,_spellcheckPos))
{
qCWarning(LOKALIZE_LOG)<<_spellcheckStartPos.entry;
qCWarning(LOKALIZE_LOG)<<_spellcheckStartPos.form;
bool continueFromStart=
!(_spellcheckStartPos.entry==0 && _spellcheckStartPos.form==0)
&& KMessageBox::questionYesNo(this,i18n("Lokalize has reached end of document. Do you want to continue from start?"),
i18nc("@title", "Spellcheck"))==KMessageBox::Yes;
if (continueFromStart)
{
_spellcheckStartPos.entry=0;
_spellcheckStartPos.form=0;
_spellcheckPos=_spellcheckStartPos;
}
else
{
KMessageBox::information(this,i18n("Lokalize has finished spellchecking"), i18nc("@title", "Spellcheck"));
return;
}
}
}
while (m_catalog->msgstr(_spellcheckPos).isEmpty() || !m_catalog->isApproved(_spellcheckPos.entry));
m_sonnetDialog->setBuffer(m_catalog->msgstr(_spellcheckPos).remove(Project::instance()->accel()));
}
void EditorTab::spellcheckStop()
{
m_spellcheckStop=true;
}
void EditorTab::spellcheckCancel()
{
m_catalog->setIndex(m_spellcheckStartUndoIndex);
gotoEntry(_spellcheckPos);
}
void EditorTab::spellcheckShow(const QString &word, int offset)
{
const Project& project = *Project::instance();
const QString accel = project.accel();
QString source=m_catalog->source(_spellcheckPos);
source.remove(accel);
if (source.contains(word) && project.targetLangCode().leftRef(2) != project.sourceLangCode().leftRef(2))
{
m_sonnetDialog->setUpdatesEnabled(false);
m_sonnetChecker->continueChecking();
return;
}
m_sonnetDialog->setUpdatesEnabled(true);
show();
DocPosition pos=_spellcheckPos;
int length=word.length();
calcOffsetWithAccels(m_catalog->target(pos),offset,length);
pos.offset=offset;
gotoEntry(pos,length);
}
void EditorTab::spellcheckReplace(QString oldWord, int offset, const QString &newWord)
{
DocPosition pos=_spellcheckPos;
int length=oldWord.length();
calcOffsetWithAccels(m_catalog->target(pos),offset,length);
pos.offset=offset;
if (length>oldWord.length())//replaced word contains accel mark
oldWord=m_catalog->target(pos).mid(offset,length);
m_catalog->push(new DelTextCmd(m_catalog,pos,oldWord));
m_catalog->push(new InsTextCmd(m_catalog,pos,newWord));
gotoEntry(pos,newWord.length());
}
diff --git a/src/editorview.cpp b/src/editorview.cpp
index fac3dd9..075571f 100644
--- a/src/editorview.cpp
+++ b/src/editorview.cpp
@@ -1,361 +1,359 @@
/* ****************************************************************************
This file is part of Lokalize (some bits of KBabel code were reused)
Copyright (C) 2007-2014 by Nick Shaforostoff
Copyright (C) 1999-2000 by Matthias Kiefer
2001-2004 by Stanislav Visnovsky
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 "editorview.h"
#include "lokalize_debug.h"
#include "xlifftextedit.h"
#include "project.h"
#include "catalog.h"
#include "cmd.h"
#include "prefs_lokalize.h"
#include "prefs.h"
-#include "kdemacros.h"
-
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifndef NOKDE
#include
#include
#include
#endif
//parent is set on qsplitter insertion
#ifndef NOKDE
LedsWidget::LedsWidget(QWidget* parent): QWidget(parent)
{
KColorScheme colorScheme(QPalette::Normal);
QHBoxLayout* layout=new QHBoxLayout(this);
layout->addStretch();
layout->addWidget(new QLabel(i18nc("@label whether entry is fuzzy","Not ready:"),this));
layout->addWidget(ledFuzzy=new KLed(colorScheme.foreground(KColorScheme::NeutralText).color()/*Qt::green*/,KLed::Off,KLed::Sunken,KLed::Rectangular));
layout->addWidget(new QLabel(i18nc("@label whether entry is untranslated","Untranslated:"),this));
layout->addWidget(ledUntr=new KLed(colorScheme.foreground(KColorScheme::NegativeText).color()/*Qt::red*/,KLed::Off,KLed::Sunken,KLed::Rectangular));
layout->addSpacing(1);
layout->addWidget(lblColumn=new QLabel(this));
layout->addStretch();
setMaximumHeight(minimumSizeHint().height());
}
void LedsWidget::contextMenuEvent(QContextMenuEvent* event)
{
QMenu menu;
menu.addAction(i18nc("@action","Hide"));
if (!menu.exec(event->globalPos()))
return; //NOTE the config doesn't seem to work
Settings::setLeds(false);
SettingsController::instance()->dirty=true;
hide();
}
void LedsWidget::cursorPositionChanged(int column)
{
lblColumn->setText(i18nc("@info:label cursor position", "Column: %1", column));
}
#endif
EditorView::EditorView(QWidget *parent,Catalog* catalog/*,keyEventHandler* kh*/)
: QSplitter(Qt::Vertical,parent)
, m_catalog(catalog)
, m_sourceTextEdit(new TranslationUnitTextEdit(catalog,DocPosition::Source,this))
, m_targetTextEdit(new TranslationUnitTextEdit(catalog,DocPosition::Target,this))
, m_pluralTabBar(new QTabBar(this))
#ifndef NOKDE
, m_leds(0)
#endif
, m_modifiedAfterFind(false)
{
m_pluralTabBar->hide();
m_sourceTextEdit->setWhatsThis(i18n("Original String
\n"
"This part of the window shows the original message\n"
"of the currently displayed entry.
"));
m_sourceTextEdit->viewport()->setBackgroundRole(QPalette::Background);
connect (m_targetTextEdit, SIGNAL(contentsModified(DocPosition)), this, SLOT(resetFindForCurrent(DocPosition)));
connect (m_targetTextEdit, SIGNAL(toggleApprovementRequested()), this, SLOT(toggleApprovement()));
connect (this, SIGNAL(signalApprovedEntryDisplayed(bool)), m_targetTextEdit, SLOT(reflectApprovementState()));
connect (m_sourceTextEdit, SIGNAL(tagInsertRequested(InlineTag)), m_targetTextEdit, SLOT(insertTag(InlineTag)));
connect (m_sourceTextEdit, SIGNAL(binaryUnitSelectRequested(QString)), this, SIGNAL(binaryUnitSelectRequested(QString)));
connect (m_targetTextEdit, SIGNAL(binaryUnitSelectRequested(QString)), this, SIGNAL(binaryUnitSelectRequested(QString)));
connect (m_sourceTextEdit, SIGNAL(gotoEntryRequested(DocPosition)), this, SIGNAL(gotoEntryRequested(DocPosition)));
connect (m_targetTextEdit, SIGNAL(gotoEntryRequested(DocPosition)), this, SIGNAL(gotoEntryRequested(DocPosition)));
connect (m_sourceTextEdit, SIGNAL(tmLookupRequested(DocPosition::Part,QString)), this, SIGNAL(tmLookupRequested(DocPosition::Part,QString)));
connect (m_targetTextEdit, SIGNAL(tmLookupRequested(DocPosition::Part,QString)), this, SIGNAL(tmLookupRequested(DocPosition::Part,QString)));
connect (m_sourceTextEdit, SIGNAL(findRequested()), this, SIGNAL(findRequested()));
connect (m_targetTextEdit, SIGNAL(findRequested()), this, SIGNAL(findRequested()));
connect (m_sourceTextEdit, SIGNAL(findNextRequested()), this, SIGNAL(findNextRequested()));
connect (m_targetTextEdit, SIGNAL(findNextRequested()), this, SIGNAL(findNextRequested()));
connect (m_sourceTextEdit, SIGNAL(replaceRequested()), this, SIGNAL(replaceRequested()));
connect (m_targetTextEdit, SIGNAL(replaceRequested()), this, SIGNAL(replaceRequested()));
connect (this, SIGNAL(doExplicitCompletion()), m_targetTextEdit, SLOT(doExplicitCompletion()));
addWidget(m_pluralTabBar);
addWidget(m_sourceTextEdit);
addWidget(m_targetTextEdit);
QWidget::setTabOrder(m_targetTextEdit,m_sourceTextEdit);
QWidget::setTabOrder(m_sourceTextEdit,m_targetTextEdit);
setFocusProxy(m_targetTextEdit);
// QTimer::singleShot(3000,this,SLOT(setupWhatsThis()));
settingsChanged();
}
EditorView::~EditorView()
{
}
void EditorView::resetFindForCurrent(const DocPosition& pos)
{
m_modifiedAfterFind=true;
emit signalChanged(pos.entry);
}
void EditorView::settingsChanged()
{
//Settings::self()->config()->setGroup("Editor");
m_sourceTextEdit->document()->setDefaultFont(Settings::msgFont());
m_targetTextEdit->document()->setDefaultFont(Settings::msgFont());
#ifndef NOKDE
if (m_leds) m_leds->setVisible(Settings::leds());
else if (Settings::leds())
{
m_leds=new LedsWidget(this);
insertWidget(2,m_leds);
connect (m_targetTextEdit, SIGNAL(cursorPositionChanged(int)), m_leds, SLOT(cursorPositionChanged(int)));
connect (m_targetTextEdit, SIGNAL(nonApprovedEntryDisplayed()),m_leds->ledFuzzy, SLOT(on()));
connect (m_targetTextEdit, SIGNAL(approvedEntryDisplayed()), m_leds->ledFuzzy, SLOT(off()));
connect (m_targetTextEdit, SIGNAL(untranslatedEntryDisplayed()),m_leds->ledUntr, SLOT(on()));
connect (m_targetTextEdit, SIGNAL(translatedEntryDisplayed()), m_leds->ledUntr, SLOT(off()));
m_targetTextEdit->showPos(m_targetTextEdit->currentPos());
}
#endif
}
//main function in this file :)
void EditorView::gotoEntry(DocPosition pos, int selection)
{
setUpdatesEnabled(false);
bool refresh=(pos.entry==-1);
if (refresh) pos=m_targetTextEdit->currentPos();
//qCWarning(LOKALIZE_LOG)<<"refresh"<isPlural(pos.entry)))
{
if (Q_UNLIKELY( m_catalog->numberOfPluralForms()!=m_pluralTabBar->count() ))
{
int i=m_pluralTabBar->count();
if (m_catalog->numberOfPluralForms()>m_pluralTabBar->count())
while (inumberOfPluralForms())
m_pluralTabBar->addTab(i18nc("@title:tab","Plural Form %1",++i));
else
while (i>m_catalog->numberOfPluralForms())
m_pluralTabBar->removeTab(i--);
}
m_pluralTabBar->show();
m_pluralTabBar->blockSignals(true);
m_pluralTabBar->setCurrentIndex(pos.form);
m_pluralTabBar->blockSignals(false);
}
else
m_pluralTabBar->hide();
//bool keepCursor=DocPos(pos)==DocPos(_msgidEdit->currentPos());
bool keepCursor=false;
CatalogString sourceWithTags=m_sourceTextEdit->showPos(pos,CatalogString(),keepCursor);
//qCWarning(LOKALIZE_LOG)<<"calling showPos";
QString targetString=m_targetTextEdit->showPos(pos,sourceWithTags,keepCursor).string;
//qCWarning(LOKALIZE_LOG)<<"ss"<<_msgstrEdit->textCursor().anchor()<<_msgstrEdit->textCursor().position();
m_sourceTextEdit->cursorToStart();
m_targetTextEdit->cursorToStart();
bool untrans=targetString.isEmpty();
//qCWarning(LOKALIZE_LOG)<<"ss1"<<_msgstrEdit->textCursor().anchor()<<_msgstrEdit->textCursor().position();
if (pos.offset || selection)
{
TranslationUnitTextEdit* msgEdit=(pos.part==DocPosition::Source?m_sourceTextEdit:m_targetTextEdit);
QTextCursor t=msgEdit->textCursor();
t.movePosition(QTextCursor::NextCharacter,QTextCursor::MoveAnchor,pos.offset);
//NOTE this was kinda bug due to on-the-fly msgid wordwrap
if (selection)
t.movePosition(QTextCursor::NextCharacter,QTextCursor::KeepAnchor,selection);
msgEdit->setTextCursor(t);
}
else if (!untrans)
{
QTextCursor t=m_targetTextEdit->textCursor();
//what if msg starts with a tag?
if (Q_UNLIKELY( targetString.startsWith('<') ))
{
int offset=targetString.indexOf(QRegExp(QStringLiteral(">[^<]")));
if ( offset!=-1 )
t.movePosition(QTextCursor::NextCharacter,QTextCursor::MoveAnchor,offset+1);
}
else if (Q_UNLIKELY( targetString.startsWith(TAGRANGE_IMAGE_SYMBOL) ))
{
int offset=targetString.indexOf(QRegExp(QStringLiteral("[^")%QChar(TAGRANGE_IMAGE_SYMBOL)%']'));
if ( offset!=-1 )
t.movePosition(QTextCursor::NextCharacter,QTextCursor::MoveAnchor,offset+1);
}
m_targetTextEdit->setTextCursor(t);
}
//qCWarning(LOKALIZE_LOG)<<"set-->"<<_msgstrEdit->textCursor().anchor()<<_msgstrEdit->textCursor().position();
//qCWarning(LOKALIZE_LOG)<<"anchor"<setFocus();
setUpdatesEnabled(true);
}
//BEGIN edit actions that are easier to do in this class
void EditorView::unwrap(TranslationUnitTextEdit* editor)
{
if (!editor)
editor=m_targetTextEdit;
QTextCursor t=editor->document()->find(QRegExp("[^(\\\\n)]$"));
if (t.isNull())
return;
if (editor==m_targetTextEdit)
m_catalog->beginMacro(i18nc("@item Undo action item","Unwrap"));
t.movePosition(QTextCursor::EndOfLine);
if (!t.atEnd())
t.deleteChar();
QRegExp rx("[^(\\\\n)>]$");
//remove '\n's skipping "\\\\n"
while (!(t=editor->document()->find(rx,t)).isNull())
{
t.movePosition(QTextCursor::EndOfLine);
if (!t.atEnd())
t.deleteChar();
}
if (editor==m_targetTextEdit)
m_catalog->endMacro();
}
void EditorView::insertTerm(const QString& term)
{
m_targetTextEdit->insertPlainText(term);
m_targetTextEdit->setFocus();
}
QString EditorView::selectionInTarget() const
{
//TODO remove IMAGES
return m_targetTextEdit->textCursor().selectedText();
}
QString EditorView::selectionInSource() const
{
//TODO remove IMAGES
return m_sourceTextEdit->textCursor().selectedText();
}
void EditorView::setProperFocus()
{
m_targetTextEdit->setFocus();
}
//END edit actions that are easier to do in this class
QObject* EditorView::viewPort()
{
return m_targetTextEdit;
}
void EditorView::toggleBookmark(bool checked)
{
if (Q_UNLIKELY( m_targetTextEdit->currentPos().entry==-1 ))
return;
m_catalog->setBookmark(m_targetTextEdit->currentPos().entry,checked);
}
void EditorView::toggleApprovement()
{
//qCWarning(LOKALIZE_LOG)<<"called";
if (Q_UNLIKELY( m_targetTextEdit->currentPos().entry==-1 ))
return;
bool newState=!m_catalog->isApproved(m_targetTextEdit->currentPos().entry);
SetStateCmd::push(m_catalog,m_targetTextEdit->currentPos(),newState);
emit signalApprovedEntryDisplayed(newState);
}
void EditorView::setState(TargetState state)
{
if (Q_UNLIKELY( m_targetTextEdit->currentPos().entry==-1
|| m_catalog->state(m_targetTextEdit->currentPos())==state))
return;
SetStateCmd::instantiateAndPush(m_catalog,m_targetTextEdit->currentPos(),state);
emit signalApprovedEntryDisplayed(m_catalog->isApproved(m_targetTextEdit->currentPos()));
}
void EditorView::setEquivTrans(bool equivTrans)
{
m_catalog->push(new SetEquivTransCmd(m_catalog, m_targetTextEdit->currentPos(), equivTrans));
}
diff --git a/src/filesearch/filesearchtab.cpp b/src/filesearch/filesearchtab.cpp
index bdb1c93..32776b4 100644
--- a/src/filesearch/filesearchtab.cpp
+++ b/src/filesearch/filesearchtab.cpp
@@ -1,958 +1,957 @@
/* ****************************************************************************
This file is part of Lokalize
Copyright (C) 2007-2014 by Nick Shaforostoff
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) version 3 or any later version
accepted by the membership of KDE e.V. (or its successor approved
by the membership of KDE e.V.), which shall act as a proxy
defined in Section 14 of version 3 of the license.
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, see .
**************************************************************************** */
#include "filesearchtab.h"
#include "lokalize_debug.h"
#include "ui_filesearchoptions.h"
#include "ui_massreplaceoptions.h"
#include "project.h"
#include "prefs.h"
#include "tmscanapi.h" //TODO separate some decls into new header
#include "state.h"
#include "qaview.h"
#include "catalog.h"
#include "fastsizehintitemdelegate.h"
-#include "kdemacros.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifndef NOKDE
#include
#include
#include
#endif
static QStringList doScanRecursive(const QDir& dir);
class FileListModel: public QStringListModel
{
public:
FileListModel(QObject* parent): QStringListModel(parent){}
QVariant data(const QModelIndex& item, int role=Qt::DisplayRole) const;
Qt::ItemFlags flags(const QModelIndex&) const {return Qt::ItemIsEnabled|Qt::ItemIsSelectable;}
};
QVariant FileListModel::data(const QModelIndex& item, int role) const
{
if (role==Qt::DisplayRole)
return shorterFilePath(stringList().at(item.row()));
if (role==Qt::UserRole)
return stringList().at(item.row());
return QVariant();
}
SearchFileListView::SearchFileListView(QWidget* parent)
: QDockWidget( i18nc("@title:window","File List"), parent)
, m_browser(new QTreeView(this))
, m_background(new QLabel(i18n("Drop translation files here..."), this))
, m_model(new FileListModel(this))
{
setWidget(m_background);
m_background->setMinimumWidth(QFontMetrics(font()).averageCharWidth()*30);
m_background->setAlignment(Qt::AlignCenter);
m_browser->hide();
m_browser->setModel(m_model);
m_browser->setRootIsDecorated(false);
m_browser->setHeaderHidden(true);
m_browser->setUniformRowHeights(true);
m_browser->setAlternatingRowColors(true);
m_browser->setContextMenuPolicy(Qt::ActionsContextMenu);
QAction* action=new QAction(i18nc("@action:inmenu", "Clear"), m_browser);
connect(action, SIGNAL(triggered()), this, SLOT(clear()));
m_browser->addAction(action);
connect(m_browser, SIGNAL(activated(QModelIndex)), this, SLOT(requestFileOpen(QModelIndex)));
}
void SearchFileListView::requestFileOpen(const QModelIndex& item)
{
emit fileOpenRequested(item.data(Qt::UserRole).toString());
}
void SearchFileListView::addFiles(const QStringList& files)
{
if (files.isEmpty())
return;
m_background->hide();
setWidget(m_browser);
m_browser->show();
//ensure unquiness, sorting the list along the way
QMap map;
foreach(const QString& filepath, m_model->stringList())
map[filepath]=true;
foreach(const QString& filepath, files)
map[filepath]=true;
m_model->setStringList(map.keys());
}
void SearchFileListView::addFilesFast(const QStringList& files)
{
if (files.size())
m_model->setStringList(m_model->stringList()+files);
}
void SearchFileListView::clear()
{
m_model->setStringList(QStringList());
}
QStringList SearchFileListView::files() const
{
return m_model->stringList();
}
void SearchFileListView::scrollTo(const QString& file)
{
if (file.isEmpty())
{
m_browser->scrollToTop();
return;
}
int idx=m_model->stringList().indexOf(file);
if (idx!=-1)
m_browser->scrollTo(m_model->index(idx, 0), QAbstractItemView::PositionAtCenter);
}
bool SearchParams::isEmpty() const
{
return sourcePattern.pattern().isEmpty()
&& targetPattern.pattern().isEmpty();
}
SearchJob::SearchJob(const QStringList& f, const SearchParams& sp, const QVector& r, int sn, QObject*)
: QRunnable()
, files(f)
, searchParams(sp)
, rules(r)
, searchNumber(sn)
, m_size(0)
{
setAutoDelete(false);
}
void SearchJob::run()
{
QTime a;a.start();
bool removeAmpFromSource = searchParams.sourcePattern.patternSyntax()==QRegExp::FixedString
&& !searchParams.sourcePattern.pattern().contains(QLatin1Char('&'));
bool removeAmpFromTarget = searchParams.targetPattern.patternSyntax()==QRegExp::FixedString
&& !searchParams.targetPattern.pattern().contains(QLatin1Char('&'));
foreach(const QString& filePath, files)
{
Catalog catalog(0);
- if (KDE_ISUNLIKELY(catalog.loadFromUrl(filePath, QString(), &m_size, true)!=0))
+ if (Q_UNLIKELY(catalog.loadFromUrl(filePath, QString(), &m_size, true)!=0))
continue;
//QVector catalogResults;
int numberOfEntries=catalog.numberOfEntries();
DocPosition pos(0);
for (;pos.entry