diff options
Diffstat (limited to 'kbabel/kbabel/kbabelview.cpp')
-rw-r--r-- | kbabel/kbabel/kbabelview.cpp | 4473 |
1 files changed, 4473 insertions, 0 deletions
diff --git a/kbabel/kbabel/kbabelview.cpp b/kbabel/kbabel/kbabelview.cpp new file mode 100644 index 00000000..2e9ebfa3 --- /dev/null +++ b/kbabel/kbabel/kbabelview.cpp @@ -0,0 +1,4473 @@ +/* **************************************************************************** + This file is part of KBabel + + Copyright (C) 1999-2000 by Matthias Kiefer <matthias.kiefer@gmx.de> + 2002-2005 by Stanislav Visnovsky <visnovsky@kde.org> + Copyright (C) 2006 by Nicolas GOUTTE <goutte@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. + +**************************************************************************** */ + +#include "kbabelview.h" + +#include "catalogfileplugin.h" +#include "toolaction.h" +#include "kbabelsettings.h" +#include "kbprojectsettings.h" + +#include <qlayout.h> + +#include <qlabel.h> +#include <qframe.h> +#include <qfileinfo.h> +#include <qvariant.h> +#include <qwhatsthis.h> +#include <qdragobject.h> +#include <qpopupmenu.h> +#include <qstylesheet.h> +#include <qtabwidget.h> +#include <qtextview.h> +#include <qtextstream.h> +#include <qtimer.h> +#include <qvbox.h> + +#include <dcopclient.h> +#include <kdeversion.h> +#include <kcharsets.h> +#include <kcmenumngr.h> +#include <kconfigdialog.h> +#include <kdatatool.h> +#include <kglobal.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kcursor.h> +#include <kfiledialog.h> +#include <kconfig.h> +#include <kapplication.h> +#include <kled.h> +#include <klistbox.h> +#include <kio/netaccess.h> +#include <knotifyclient.h> +#include <ktempfile.h> +#include <kspell.h> +#include <kwin.h> +#include <kstdaccel.h> +#include <kurldrag.h> +#include <kglobalsettings.h> + +#include "resources.h" + +#include "editcmd.h" +#include "finddialog.h" +#include "gotodialog.h" +#include "headereditor.h" +#include "hidingmsgedit.h" +#include "kbabeldictbox.h" +#include "kbcatalog.h" +#include "msgfmt.h" +#include "spelldlg.h" +#include "regexpextractor.h" +#include "projectprefwidgets.h" +#include "kbabel.h" +#include "kbprojectmanager.h" + +#include "commentview.h" +#include "contextview.h" +#include "kbcataloglistview.h" +#include "charselectview.h" +#include "taglistview.h" +#include "sourceview.h" +#include "errorlistview.h" + +#include "version.h" + + +#define ID_DROP_OPEN 1 +#define ID_DROP_OPEN_TEMPLATE 2 + +#define MAX_HISTORY 50 + +using namespace KBabel; + +QPtrList<KBabelView> *KBabelView::viewList = 0; + +KBabelView::KBabelView(KBCatalog* catalog,KBabelMW *parent, Project::Ptr project) + : QWidget(parent) + , _redirectedBackSearch (false) + , _project (project) + , m_mainwindow (parent) + , m_sourceview (0) +{ + if(!viewList) + viewList = new QPtrList<KBabelView>; + + viewList->append(this); + + if (catalog == 0) + kdFatal(KBABEL) << "catalog==0" << endl; + + _catalog=catalog; + _catalog->registerView(this); + + _config = KSharedConfig::openConfig ("kbabelrc"); + + KConfigGroupSaver gs(_config,"Editor"); + bool buildLeds=! KBabelSettings::ledInStatusbar(); + + _fuzzyLed=0; + _untransLed=0; + _errorLed=0; + _currentIndex=0; + _currentPos.item=1; // to ensure update + _currentPos.form=0; + _gotoDialog=0; + _findDialog=0; + _replaceDialog=0; + _replaceAskDialog=0; + _tagsMenu=0; + _argsMenu=0; + _autoSearchTempDisabled=false; + _diffEnabled=false; + _loadingDiffFile=false; + _diffing=false; + _dontBeep=false; + + m_overwrite = false; + + autoSaveTimer = 0; + + _showTryLaterBox=true; + _tagExtractor = new RegExpExtractor( catalog->tagSettings().tagExpressions ); + _argExtractor = new RegExpExtractor( catalog->tagSettings().argExpressions ); + + _editingDocumentation=false; + + _autocheckTools.clear(); + _autocheckTools.setAutoDelete(true); + + spell.posDict.setAutoDelete(true); + spell.active=false; + + spell2.kspell = 0; + spell2.config = 0; + + setAcceptDrops(true); + + // this widget does not contain anything, we should hide it + hide(); + + dictBox = new KBabelDictBox(this,"dictBox"); + QWhatsThis::add(dictBox, + i18n("<qt><p><b>Search results</b></p>" + "<p>This part of the window shows the results of searching in " + "dictionaries.<p>" + "<p>In the top is displayed the number of entries found and " + "where the currently displayed entry is found. Use the buttons " + "at the bottom to navigate through the search results.</p>" + "<p>Search is either started automatically when switching to " + "another entry in the editor window or by choosing the desired " + "dictionary in <b>Dictionaries->Find...</b>.</p>" + "<p>The common options can be configured in the preferences dialog " + "in section <b>Search</b> and the options for the different " + "dictionaries can be changed with " + "<b>Settings->Configure Dictionary</b>.</p></qt>")); + + initDockWidgets(); + + msgstrEdit->setReadOnly(true); + dictBox->setEnabled(false); + + connect(this, SIGNAL(signalNewFileOpened(KURL)), + m_cataloglistview, SLOT(slotNewFileOpened())); + + connect(msgstrEdit,SIGNAL(signalUndoCmd(KBabel::EditCommand*)),this,SLOT(forwardMsgstrEditCmd(KBabel::EditCommand*))); + connect(msgstrEdit,SIGNAL(textChanged()),this + ,SIGNAL(signalMsgstrChanged())); + + connect(msgstrEdit,SIGNAL(textChanged(const QString&)),m_cataloglistview + ,SLOT(msgstrChanged(const QString&))); + + connect(this,SIGNAL(signalMsgstrChanged()),this,SLOT(autoCheck())); + connect(msgstrEdit,SIGNAL(currentFormChanged(uint)), this + ,SLOT(msgstrPluralFormChanged(uint))); + + connect(msgidLabel,SIGNAL(cursorPositionChanged(int,int)) + , this, SIGNAL(signalCursorPosChanged(int,int))); + connect(msgstrEdit,SIGNAL(cursorPositionChanged(int,int)) + , this, SIGNAL(signalCursorPosChanged(int,int))); + + connect(dictBox,SIGNAL(searchStarted()),this + ,SLOT(forwardSearchStart())); + connect(dictBox, SIGNAL(progressStarts(const QString&)), this + ,SLOT(forwardProgressStart(const QString&))); + connect(dictBox,SIGNAL(progressed(int)),this + ,SIGNAL(signalProgress(int))); + connect(dictBox,SIGNAL(searchStopped()),this + ,SLOT(forwardSearchStop())); + connect(dictBox,SIGNAL(progressEnds()),this + ,SIGNAL(signalClearProgressBar())); + + connect(dictBox,SIGNAL(modulesChanged()),this, + SIGNAL(signalDictionariesChanged())); + connect(dictBox,SIGNAL(errorInModule(const QString&)),this + ,SLOT(showError(const QString&))); + + connect(_catalog,SIGNAL(signalSettingsChanged(KBabel::IdentitySettings)), + this, SLOT(setNewLanguage())); + + connect(_catalog,SIGNAL(signalNumberOfFuzziesChanged(uint)), + this, SLOT(checkFuzzies())); + connect(_catalog,SIGNAL(signalNumberOfUntranslatedChanged(uint)), + this, SLOT(checkUntranslated())); + + if(buildLeds) + { + connect(this,SIGNAL(signalFuzzyDisplayed(bool)) + ,this,SLOT(toggleFuzzyLed(bool))); + connect(this,SIGNAL(signalUntranslatedDisplayed(bool)) + ,this,SLOT(toggleUntransLed(bool))); + connect(this,SIGNAL(signalFaultyDisplayed(bool)) + ,this,SLOT(toggleErrorLed(bool))); + } + + _dropMenu = new QPopupMenu(this); + _dropMenu->insertItem(i18n("Menu item", "Open"),ID_DROP_OPEN); + _dropMenu->insertItem(i18n("Open Template"),ID_DROP_OPEN_TEMPLATE); + + readSettings(_config); + readProject(_project); + + connect (project, SIGNAL(signalSpellcheckSettingsChanged()), + this, SLOT(updateProjectSettings())); + + if(!_catalog->currentURL().isEmpty()) + { + newFileOpened(_catalog->isReadOnly()); + } + + // take over the language settings from the catalog + setNewLanguage(); +} + +KBabelView::~KBabelView() +{ + viewList->remove(this); + if(viewList->isEmpty()) + { + delete viewList; + viewList=0; + } + + _catalog->removeView(this); + + // check if this view was the last view and delete the catalog if necessary + if(!_catalog->hasView()) + { + delete _catalog; + } + + delete _argExtractor; + delete _tagExtractor; + + if( spell2.kspell ) + { + spell2.kspell = 0; + delete spell2.config; + spell2.config = 0; + } +} + +void KBabelView::initDockWidgets() +{ + // setup main dock widget - original text + QWidget *tempWidget=new QWidget(this,"msgidWidget"); + tempWidget->setMinimumSize(350,150); + + QVBoxLayout *layout=new QVBoxLayout(tempWidget); + + msgidLabel = new HidingMsgEdit(2, this, 0, tempWidget,"msgidLabel"); + msgidLabel->installEventFilter(this); + msgidLabel->setReadOnly(true); + msgidLabel->setDiffMode(true); + KCursor::setAutoHideCursor(msgidLabel,true); + + msgidLabel->setText(i18n("KBabel Version %1\n" +"Copyright 1999-%2 by KBabel developers.\n" +" Matthias Kiefer <kiefer@kde.org>\n" +" Stanislav Visnovsky <visnovsky@kde.org>\n" +" Marco Wegner <dubbleu@web.de>\n" +" Dwayne Bailey <dwayne@translate.org.za>\n" +" Andrea Rizzi <rizzi@kde.org>\n\n" +"Any comments, suggestions, etc. should be sent to the mailing list <kbabel@kde.org>.\n\n" +"This program is licensed under the terms of the GNU GPL.\n\n" +"Special thanks to Thomas Diehl for many hints to the GUI\n" +"and the behavior of KBabel and to Stephan Kulow, who always\n" +"lends me a helping hand.\n\n" +"Many good ideas, especially for the Catalog Manager, are taken\n" +"from KTranslator by Andrea Rizzi.").arg(VERSION).arg(2006)); + + QLabel *label=new QLabel(msgidLabel,i18n("O&riginal string (msgid):"),tempWidget); + + QHBoxLayout* hb=new QHBoxLayout(layout); + hb->addSpacing(KDialog::marginHint()); + hb->addWidget(label); + + layout->addWidget(msgidLabel); + layout->setStretchFactor(msgidLabel,1); + + QWhatsThis::add(tempWidget, + i18n("<qt><p><b>Original String</b></p>\n\ +<p>This part of the window shows the original message\n\ +of the currently displayed entry.</p></qt>")); + + KDockWidget* mainDock; + mainDock = m_mainwindow->createDockWidget( "Original", QPixmap ()); + //i18n: translators: Dock window caption + mainDock->setCaption(i18n("Original Text")); + mainDock->setGeometry(50, 50, 100, 100); + // forbit docking abilities of mainDock itself + mainDock->setEnableDocking(KDockWidget::DockFullSite); + + mainDock->setWidget(tempWidget); + m_mainwindow->setMainDockWidget(mainDock); // master dockwidget + mainDock->show (); + m_mainwindow->setView (mainDock); + connect (this, SIGNAL (signalCopy ()), this, SLOT (textCopy ())); + connect (this, SIGNAL (signalCut ()), this, SLOT (textCut ())); + connect (this, SIGNAL (signalPaste ()), this, SLOT (textPaste ())); + connect (this, SIGNAL (signalSelectAll ()), this, SLOT (selectAll ())); + + KDockWidget* comment_dock = m_mainwindow->createDockWidget( "Comment", QPixmap ()); + //i18n: translators: Dock window caption + comment_dock->setCaption(i18n("Comment")); + comment_dock->setGeometry(50, 50, 100, 100); + comment_dock->setEnableDocking(KDockWidget::DockCorner); + m_commentview = new CommentView(_catalog, comment_dock, _project); + comment_dock->setWidget(m_commentview); + comment_dock->manualDock( mainDock, // dock target + KDockWidget::DockRight, // dock site + 20 ); // relation target/this (in percent) + comment_dock->show (); + connect (this, SIGNAL (signalCopy ()), m_commentview, SLOT (textCopy ())); + connect (this, SIGNAL (signalCut ()), m_commentview, SLOT (textCut ())); + connect (this, SIGNAL (signalPaste ()), m_commentview, SLOT (textPaste ())); + connect (this, SIGNAL (signalSelectAll ()), m_commentview, SLOT (textSelectAll ())); + m_commentview->installEventFilter( this ); + + // build the msgstr widget + tempWidget=new QWidget(this,"msgstrWidget"); + tempWidget->setMinimumSize(350,150); + + layout=new QVBoxLayout(tempWidget); + + // if undefined number of plural forms, use 1 + int pf = _catalog->defaultNumberOfPluralForms(); + if( pf < 1 ) + pf = 1; + + msgstrEdit = new HidingMsgEdit( pf,this,spell2.kspell,tempWidget,"msgstrEdit"); + msgstrEdit->installEventFilter(this); + KCursor::setAutoHideCursor(msgstrEdit,true); + + label=new QLabel(msgstrEdit,i18n("Trans&lated string (msgstr):"),tempWidget); + + hb=new QHBoxLayout(layout); + hb->setSpacing(KDialog::spacingHint()); + + hb->addSpacing(KDialog::marginHint()); + hb->addWidget(label); + hb->addStretch(1); + + if(! KBabelSettings::ledInStatusbar()) + { + _fuzzyLed = new KLed(Qt::red,KLed::Off,KLed::Sunken,KLed::Rectangular + ,tempWidget); + _fuzzyLed->setFixedSize(15,12); + label = new QLabel(i18n("fuzzy"),tempWidget); + hb->addWidget(_fuzzyLed); + hb->addWidget(label); + + hb->addSpacing(KDialog::spacingHint()); + + _untransLed = new KLed(Qt::red,KLed::Off,KLed::Sunken,KLed::Rectangular + ,tempWidget); + _untransLed->setFixedSize(15,12); + label = new QLabel(i18n("untranslated"),tempWidget); + hb->addWidget(_untransLed); + hb->addWidget(label); + + hb->addSpacing(KDialog::marginHint()); + + _errorLed = new KLed(Qt::red,KLed::Off,KLed::Sunken,KLed::Rectangular + ,tempWidget); + _errorLed->setFixedSize(15,12); + label = new QLabel(i18n("faulty"),tempWidget); + hb->addWidget(_errorLed); + hb->addWidget(label); + + hb->addSpacing(KDialog::marginHint()); + + hb->addStretch(1); + + // ### TODO: perhaps it should be moreprecise where the setting can be changed + QString ledMsg=i18n("<qt><p><b>Status LEDs</b></p>\n" + "<p>These LEDs display the status of the currently displayed message.\n" + "You can change their color in the preferences dialog section\n" + "<b>Editor</b> on page <b>Appearance</b></p></qt>"); + QWhatsThis::add(_fuzzyLed,ledMsg); + QWhatsThis::add(_untransLed,ledMsg); + QWhatsThis::add(_errorLed,ledMsg); + } + + layout->addWidget(msgstrEdit); + layout->setStretchFactor(msgstrEdit,1); + + QWhatsThis::add(tempWidget, + i18n("<qt><p><b>Translation Editor</b></p>\n\ +<p>This editor displays and lets you edit the translation of the currently displayed message.<p></qt>")); + + + KDockWidget* msgstr_dock = m_mainwindow->createDockWidget( "Msgstr", QPixmap ()); + //i18n: translators: Dock window caption + msgstr_dock->setCaption(i18n("Translated String")); + msgstr_dock->setEnableDocking(KDockWidget::DockCorner); + msgstr_dock->setWidget(tempWidget); + msgstr_dock->manualDock( mainDock, // dock target + KDockWidget::DockBottom, // dock site + 50 ); // relation target/this (in percent) + msgstr_dock->show (); + + KDockWidget* dock = m_mainwindow->createDockWidget( "Search", QPixmap ()); + //i18n: translators: Dock window caption + dock->setCaption(i18n("the search (noun)","Search")); + //i18n: translators: Dock tab caption + dock->setTabPageLabel(i18n("the search (noun)","Se&arch")); + dock->setGeometry(50, 50, 100, 100); + dock->setWidget(dictBox); + dock->manualDock( comment_dock, // dock target + KDockWidget::DockBottom, // dock site + 20 ); // relation target/this (in percent) + dock->show (); + + KDockWidget* tools = dock; + + dock = m_mainwindow->createDockWidget( "PO context", QPixmap ()); + //i18n: translators: Dock window caption + dock->setCaption(i18n("PO Context")); + //i18n: translators: Dock tab caption + dock->setTabPageLabel(i18n("PO C&ontext")); + dock->setGeometry(50, 50, 100, 100); + ContextView * m_contextview = new ContextView(_catalog, dock, _project); + dock->setWidget(m_contextview); + dock->manualDock( tools, // dock target + KDockWidget::DockCenter, // dock site + 20 ); // relation target/this (in percent) + dock->show (); + + dock = m_mainwindow->createDockWidget( "Charselector", QPixmap ()); + //i18n: translators: Dock window caption + dock->setCaption(i18n("Character Table")); + //i18n: translators: Dock tab caption + dock->setTabPageLabel(i18n("C&hars")); + dock->setGeometry(50, 50, 100, 100); + m_charselectorview = new CharacterSelectorView(_catalog, dock, _project); + dock->setWidget(m_charselectorview); + dock->manualDock( tools, // dock target + KDockWidget::DockCenter, // dock site + 20 ); // relation target/this (in percent) + dock->show (); + + + dock = m_mainwindow->createDockWidget( "Tag List", QPixmap ()); + //i18n: translators: Dock window caption + dock->setCaption(i18n("Tag List")); + //i18n: translators: Dock tab caption + dock->setTabPageLabel(i18n("Tags")); + dock->setGeometry(50, 50, 100, 100); + TagListView* m_taglistview = new TagListView(_catalog, dock, _project); + dock->setWidget(m_taglistview); + dock->manualDock( tools, // dock target + KDockWidget::DockCenter, // dock site + 20 ); // relation target/this (in percent) + dock->show (); + + dock = m_mainwindow->createDockWidget( "Source Context", QPixmap ()); + //i18n: translators: Dock window caption + dock->setCaption(i18n("Source Context")); + //i18n: translators: Dock tab caption + dock->setTabPageLabel(i18n("Source")); + dock->setGeometry(50, 50, 100, 100); + m_sourceview = new SourceView(_catalog, dock, _project); + dock->setWidget(m_sourceview); + dock->manualDock( tools, // dock target + KDockWidget::DockCenter, // dock site + 20 ); // relation target/this (in percent) + dock->show (); + + KDockWidget* translist_dock = m_mainwindow->createDockWidget( "Translation List", QPixmap ()); + translist_dock->setCaption(i18n("Translation List")); + translist_dock->setGeometry(50, 50, 100, 100); + translist_dock->setEnableDocking(KDockWidget::DockFullSite); + m_cataloglistview = new KBCatalogListView(_catalog, translist_dock, _project); + translist_dock->setWidget(m_cataloglistview); + translist_dock->manualDock( mainDock, KDockWidget::DockTop,100); + translist_dock->show (); + + dock = m_mainwindow->createDockWidget( "Error List", QPixmap ()); + //i18n: translators: Dock window caption + dock->setCaption(i18n("Error List")); + //i18n: translators: Dock tab caption + dock->setTabPageLabel(i18n("Errors")); + dock->setGeometry(50, 50, 100, 100); + ErrorListView* m_errorlistview = new ErrorListView(_catalog, dock, _project); + dock->setWidget(m_errorlistview); + dock->manualDock( tools, // dock target + KDockWidget::DockCenter, // dock site + 20 ); + dock->show (); + + connect(m_cataloglistview,SIGNAL(signalSelectionChanged(const KBabel::DocPosition&)) + ,this,SLOT(gotoEntry(const KBabel::DocPosition&))); + connect(this,SIGNAL(signalDisplayed(const KBabel::DocPosition&)) + ,m_commentview,SLOT(gotoEntry(const KBabel::DocPosition&))); + connect(this,SIGNAL(signalDisplayed(const KBabel::DocPosition&)) + ,m_contextview,SLOT(gotoEntry(const KBabel::DocPosition&))); + connect(this,SIGNAL(signalDisplayed(const KBabel::DocPosition&)) + ,m_taglistview,SLOT(gotoEntry(const KBabel::DocPosition&))); + connect(this,SIGNAL(signalDisplayed(const KBabel::DocPosition&)) + ,m_sourceview,SLOT(gotoEntry(const KBabel::DocPosition&))); + connect(this,SIGNAL(signalDisplayed(const KBabel::DocPosition&)) + ,m_errorlistview,SLOT(gotoEntry(const KBabel::DocPosition&))); + connect(this,SIGNAL(signalFaultyDisplayed(bool)) + ,m_errorlistview,SLOT(updateView())); + connect(m_charselectorview, SIGNAL( characterDoubleClicked(QChar) ) + ,this, SLOT( insertChar(QChar) )); + connect(m_taglistview,SIGNAL(signalTagSelected(const QString&)) + , this, SLOT(insertTagFromTool(const QString&))); + connect(m_taglistview,SIGNAL(signalHighlightedTagChanged(int)) + , this, SLOT(skipToTagFromTool(int))); + connect(this, SIGNAL(signalNextTag(int)) + , m_taglistview, SLOT(highlightTag(int))); + connect(m_commentview,SIGNAL(signalCursorPosChanged(int,int)) + , m_mainwindow, SLOT(updateCursorPosition(int,int))); +} + +KBabelView *KBabelView::viewForURL(const KURL& url, const QString project) +{ + if(url.isEmpty()) + return 0; + + if(!viewList) + return 0; + + KURL u = url; + u.cleanPath(); + + QPtrListIterator<KBabelView> it(*viewList); + KBabelView *view=0; + while( it.current() && !view) + { + KURL cu = it.current()->currentURL(); + cu.cleanPath(); + + if(cu == u && it.current()->project()==project) + { + view = it.current(); + } + + ++it; + } + + return view; +} + +KBabelView *KBabelView::emptyView(const QString) +{ + if(!viewList) + return 0; + + QPtrListIterator<KBabelView> it(*viewList); + KBabelView *view=0; + while( it.current() && !view) + { + KURL cu = it.current()->currentURL(); + if( cu.isEmpty() ) + return it.current(); + ++it; + } + + return 0; +} + +KURL KBabelView::currentURL() const +{ + return _catalog->currentURL(); +} + +bool KBabelView::isLastView() const +{ + return _catalog->isLastView(); +} + +bool KBabelView::isModified() const +{ + return _catalog->isModified(); +} + +bool KBabelView::isReadOnly() const +{ + return _catalog->isReadOnly(); +} + +bool KBabelView::isOverwriteMode() const +{ + return m_overwrite; +} + + +void KBabelView::setOverwriteMode(bool ovr) +{ + m_overwrite = ovr; + msgstrEdit->setOverwriteMode(ovr); + m_commentview->setOverwriteMode(ovr); +} + + +bool KBabelView::isSearching() const +{ + return dictBox->isSearching(); +} + + +void KBabelView::update(EditCommand* cmd, bool undo) +{ + if((int)_currentIndex==cmd->index()) + { + emitEntryState(); + + if(cmd->part()==Msgstr) + { + msgstrEdit->processCommand(cmd,undo); + emit signalMsgstrChanged(); + } + } +} + + + +void KBabelView::readSettings(KConfig* config) +{ + + if(KBabelSettings::autoUnsetFuzzy()) + { + connect(msgstrEdit,SIGNAL(textChanged()) + ,this,SLOT(autoRemoveFuzzyStatus())); + } + + setupAutoCheckTools(); + + KConfigGroupSaver saver(config,"Editor"); + + _diffEnabled = config->readBoolEntry("AutoDiff", false); + emit signalDiffEnabled(_diffEnabled); + + if(_fuzzyLed) + { + _fuzzyLed->setColor(KBabelSettings::ledColor()); + } + if(_untransLed) + { + _untransLed->setColor(KBabelSettings::ledColor()); + } + if(_errorLed) + { + _errorLed->setColor(KBabelSettings::ledColor()); + } + + msgstrEdit->setCleverEditing(KBabelSettings::cleverEditing()); + + msgstrEdit->setHighlightBg(KBabelSettings::highlightBackground()); + msgidLabel->setHighlightBg(KBabelSettings::highlightBackground()); + + msgstrEdit->setHighlightSyntax(KBabelSettings::highlightSyntax()); + msgidLabel->setHighlightSyntax(KBabelSettings::highlightSyntax()); + + msgstrEdit->setQuotes(KBabelSettings::enableQuotes()); + msgidLabel->setQuotes(KBabelSettings::enableQuotes()); + + msgstrEdit->setSpacePoints(KBabelSettings::whitespacePoints()); + msgidLabel->setSpacePoints(KBabelSettings::whitespacePoints()); + + msgstrEdit->setFont(KBabelSettings::msgFont()); + msgidLabel->setFont(KBabelSettings::msgFont()); + + msgstrEdit->setBgColor(KBabelSettings::backgroundColor()); + msgidLabel->setBgColor(KBabelSettings::backgroundColor()); + + msgstrEdit->setHighlightColors(KBabelSettings::quotedColor(),KBabelSettings::errorColor() + ,KBabelSettings::cformatColor(),KBabelSettings::accelColor(),KBabelSettings::tagColor()); + msgidLabel->setHighlightColors(KBabelSettings::quotedColor(),KBabelSettings::errorColor() + ,KBabelSettings::cformatColor(),KBabelSettings::accelColor(),KBabelSettings::tagColor()); + + msgidLabel->setDiffDisplayMode( KBabelSettings::diffAddUnderline() + ,KBabelSettings::diffDelStrikeOut() ); + msgidLabel->setDiffColors(KBabelSettings::diffAddColor(), KBabelSettings::diffDelColor() ); +} + +void KBabelView::updateProjectSettings() +{ + readProject(_project); +} + +void KBabelView::readProject(Project::Ptr project) +{ + _spellcheckSettings = project->spellcheckSettings(); + + if( _spellcheckSettings.onFlySpellcheck ) + { + + // if there is a spellchecker already, free it + if( spell2.kspell ) + { + // ensure the spellchecker is not used anymore + msgstrEdit->setSpellChecker(0L); + + // free it + spell2.kspell->cleanUp(); + + delete spell2.kspell; + spell2.kspell = 0; + } + + spell2.config = new KSpellConfig(0L, "tempSpellConfig"); + spell2.config->setNoRootAffix(_spellcheckSettings.noRootAffix); + spell2.config->setRunTogether(_spellcheckSettings.runTogether); + spell2.config->setClient(_spellcheckSettings.spellClient); + spell2.config->setEncoding(_spellcheckSettings.spellEncoding); + spell2.config->setDictionary(_spellcheckSettings.spellDict); + + spell2.kspell= new KSpell(this, "", this, SLOT(dummy(KSpell *)), + spell2.config, false, false); + if(spell2.kspell->status() == KSpell::Error) + kdWarning(KBABEL) << "Something's wrong with KSpell, can't start on-the-fly checking" << endl; + else + { + kdDebug() << "On the fly spellchecker: " + << spell2.kspell << endl; + msgstrEdit->setSpellChecker(spell2.kspell); + } + + // spell2.kspell->setAutoDelete(true); // let KSpell handle delete + //on-the-fly spellcheck end + } + else + { + // turn off spellchecker + msgstrEdit->setSpellChecker(0); + // invalidate the current settings, to make sure they are updated when needed + _spellcheckSettings.valid = false; + } + + dictBox->readSettings(project->config()); + + m_sourceview->setProject(project); +} + +void KBabelView::saveSettings() +{ + KConfigGroupSaver saver(_config,"Editor"); + + _config->writeEntry("AutoDiff",_diffEnabled); + + dictBox->saveSettings( _project->config() ); + + _catalog->savePreferences(); + + _config->sync(); + _project->config()->sync(); + + KBabelSettings::writeConfig(); +} + +void KBabelView::updateSettings() +{ + msgstrEdit->setFont(KBabelSettings::msgFont()); + msgidLabel->setFont(KBabelSettings::msgFont()); + + msgidLabel->setDiffDisplayMode( KBabelSettings::diffAddUnderline() + ,KBabelSettings::diffDelStrikeOut()); + msgidLabel->setDiffColors(KBabelSettings::diffAddColor(), KBabelSettings::diffDelColor()); + + if( _diffEnabled && !_catalog->currentURL().isEmpty() ) + { + diffShowOrig (); + diff(); + } + + msgstrEdit->setBgColor(KBabelSettings::backgroundColor()); + msgidLabel->setBgColor(KBabelSettings::backgroundColor()); + + msgstrEdit->setHighlightColors(KBabelSettings::quotedColor(),KBabelSettings::errorColor() + ,KBabelSettings::cformatColor(),KBabelSettings::accelColor(),KBabelSettings::tagColor()); + msgidLabel->setHighlightColors(KBabelSettings::quotedColor(),KBabelSettings::errorColor() + ,KBabelSettings::cformatColor(),KBabelSettings::accelColor(),KBabelSettings::tagColor()); + + msgidLabel->setDiffDisplayMode( KBabelSettings::diffAddUnderline() + ,KBabelSettings::diffDelStrikeOut()); + msgidLabel->setDiffColors(KBabelSettings::diffAddColor(), KBabelSettings::diffDelColor()); + + if(_fuzzyLed) + { + _fuzzyLed->setColor(KBabelSettings::ledColor()); + } + if(_untransLed) + { + _untransLed->setColor(KBabelSettings::ledColor()); + } + if(_errorLed) + { + _errorLed->setColor(KBabelSettings::ledColor()); + } + + disconnect(msgstrEdit,SIGNAL(textChanged()) + ,this,SLOT(autoRemoveFuzzyStatus())); + + if(KBabelSettings::autoUnsetFuzzy()) + { + connect(msgstrEdit,SIGNAL(textChanged()) + ,this,SLOT(autoRemoveFuzzyStatus())); + } + + msgstrEdit->setCleverEditing(KBabelSettings::cleverEditing()); + + msgstrEdit->setHighlightBg(KBabelSettings::highlightBackground()); + msgidLabel->setHighlightBg(KBabelSettings::highlightBackground()); + msgstrEdit->setHighlightSyntax(KBabelSettings::highlightSyntax()); + msgidLabel->setHighlightSyntax(KBabelSettings::highlightSyntax()); + + msgstrEdit->setQuotes(KBabelSettings::enableQuotes()); + msgidLabel->setQuotes(KBabelSettings::enableQuotes()); + + msgstrEdit->setSpacePoints(KBabelSettings::whitespacePoints()); + msgidLabel->setSpacePoints(KBabelSettings::whitespacePoints()); + + msgstrEdit->setFont(KBabelSettings::msgFont()); + msgidLabel->setFont(KBabelSettings::msgFont()); + + msgstrEdit->setBgColor(KBabelSettings::backgroundColor()); + msgidLabel->setBgColor(KBabelSettings::backgroundColor()); + + msgstrEdit->setHighlightColors(KBabelSettings::quotedColor(),KBabelSettings::errorColor() + ,KBabelSettings::cformatColor(),KBabelSettings::accelColor(),KBabelSettings::tagColor()); + msgidLabel->setHighlightColors(KBabelSettings::quotedColor(),KBabelSettings::errorColor() + ,KBabelSettings::cformatColor(),KBabelSettings::accelColor(),KBabelSettings::tagColor()); + + msgidLabel->setDiffDisplayMode( KBabelSettings::diffAddUnderline() + ,KBabelSettings::diffDelStrikeOut()); + msgidLabel->setDiffColors(KBabelSettings::diffAddColor(), KBabelSettings::diffDelColor()); + + // if errors should not use special color, reset the color of the text + if(! KBabelSettings::autoCheckColorError()) + { + msgstrEdit->setCurrentColor( MsgMultiLineEdit::NormalColor); + } + + if(_fuzzyLed) + { + _fuzzyLed->setColor(KBabelSettings::ledColor()); + } + if(_untransLed) + { + _untransLed->setColor(KBabelSettings::ledColor()); + } + if(_errorLed) + { + _errorLed->setColor(KBabelSettings::ledColor()); + } + + emit ledColorChanged(KBabelSettings::ledColor()); + + setupAutoCheckTools(); + + if( _catalog->numberOfEntries() > 0 ) + autoCheck(false); + +} + +void KBabelView::setupAutoCheckTools() +{ + _autocheckTools.clear(); + + kdDebug () << "Autocheck tools: " << KBabelSettings::autoCheckTools().join(",") << endl; + + if(!KBabelSettings::autoCheckTools().isEmpty() ) + { + QValueList<KDataToolInfo> tools = ToolAction::validationTools(); + + QValueList<KDataToolInfo>::Iterator it; + for( it=tools.begin(); it!=tools.end() ; ++it ) + { + if(KBabelSettings::autoCheckTools().contains((*it).service()->library()) ) + { + // maybe we can reuse the tools + KDataTool* t = (*it).createTool(); + + if( t ) _autocheckTools.append( t ); + } + } + } +} + +void KBabelView::setRMBEditMenu(QPopupMenu* popup) +{ + msgidLabel->setContextMenu( popup ); + msgstrEdit->setContextMenu( popup ); + KContextMenuManager::insert(this,popup); +} + +void KBabelView::setRMBSearchMenu(QPopupMenu* popup) +{ + dictBox->setRMBMenu(popup); +} + + +void KBabelView::saveView(KConfig *) +{ +} + +void KBabelView::restoreView(KConfig *) +{ +} + +void KBabelView::saveSession(KConfig* config) +{ + QString focus; + int line=0,col=0; + if(msgstrEdit->hasFocus()) + { + focus="msgstr"; + msgstrEdit->getCursorPosition(&line,&col); + } + else if(msgidLabel->hasFocus()) + { + focus="msgid"; + msgidLabel->getCursorPosition(&line,&col); + } + else if(dictBox->hasFocus()) + { + focus="searchbox"; + } + + config->writeEntry("Focus",focus); + config->writeEntry("CursorLine",line); + config->writeEntry("CursorCol",col); + config->writeEntry("Index",_currentIndex); + config->writeEntry("PluralForm",_currentPos.form); + + config->writePathEntry("URL",currentURL().url()); + + config->writeEntry("AutoDiff",_diffEnabled); + + if( _spellcheckSettings.valid ) + { + KConfigGroupSaver (config, "Spellcheck"); + config->writeEntry("NoRootAffix",_spellcheckSettings.noRootAffix); + config->writeEntry("RunTogether",_spellcheckSettings.runTogether); + config->writeEntry("SpellEncoding",_spellcheckSettings.spellEncoding); + config->writeEntry("SpellClient",_spellcheckSettings.spellClient); + config->writeEntry("SpellDictionary",_spellcheckSettings.spellDict); + config->writeEntry("RememberIgnored",_spellcheckSettings.rememberIgnored); + config->writePathEntry("IgnoreURL",_spellcheckSettings.ignoreURL); + } + + saveView(config); +} + +void KBabelView::restoreSession(KConfig* config) +{ + QString url=config->readPathEntry("URL"); + + if(!url.isEmpty()) + { + open(KURL( url ), QString::null, false,true); + } + + + _diffEnabled = config->readBoolEntry("AutoDiff", false); + emit signalDiffEnabled(_diffEnabled); + + msgidLabel->setDiffDisplayMode( KBabelSettings::diffAddUnderline() + , KBabelSettings::diffDelStrikeOut() ); + msgidLabel->setDiffColors( KBabelSettings::diffAddColor() + , KBabelSettings::diffDelColor() ); + + // go to last displayed entry + DocPosition pos; + pos.item=config->readNumEntry("Index"); + pos.form=config->readNumEntry("PluralForm"); + gotoEntry(pos); + + QString focus=config->readEntry("Focus"); + int line=config->readNumEntry("CursorLine"); + int col=config->readNumEntry("CursorCol"); + if(focus=="msgstr") + { + msgstrEdit->setFocus(); + msgstrEdit->setCursorPosition(line,col); + } + else if(focus=="msgid") + { + msgidLabel->setFocus(); + msgstrEdit->setCursorPosition(line,col); + } + else if(focus=="searchbox") + { + dictBox->setFocus(); + } + + restoreView(config); +} + +void KBabelView::newFileOpened(bool readOnly) +{ + // disable editing for empty files + if(_catalog->numberOfEntries() == 0) + readOnly = true; + + if(_gotoDialog) + { + _gotoDialog->setMax(_catalog->numberOfEntries()); + } + + dictBox->setDisabled(readOnly); + msgstrEdit->setReadOnly(readOnly); + msgstrEdit->setFocus(); + + QString caption=_catalog->package(); + if(readOnly) + caption+=i18n(" [readonly]"); + emit signalChangeCaption(caption); + emit signalNewFileOpened(_catalog->currentURL()); + + dictBox->setEditedPackage(_catalog->packageDir()+_catalog->packageName()); + dictBox->setEditedFile(_catalog->currentURL().url()); + + _editingDocumentation = _catalog->isGeneratedFromDocbook(); + + _backHistory.clear(); + emit signalBackHistory(false); + _forwardHistory.clear(); + emit signalForwardHistory(false); + + DocPosition pos; + pos.item=0; + pos.form=0; + _autoSearchTempDisabled=true; + gotoEntry(pos,false); + _autoSearchTempDisabled=false; + + emit signalDisplayed(pos); + + if(isActiveWindow() && KBabelSettings::autoSearch()) + { + startSearch(true); + } +} + +void KBabelView::open() +{ + open(KURL()); +} + +void KBabelView::open(const KURL& _url, const QString & package, bool checkIfModified, bool newView) +{ +#if KDE_IS_VERSION( 3, 5, 0) + KURL url = KIO::NetAccess::mostLocalURL(_url,this); +#else + KURL url = _url; +#endif + url.cleanPath(); + + KURL cu = currentURL(); + cu.cleanPath(); + if(checkIfModified && !url.isEmpty() && cu==url) + { + KWin::activateWindow(topLevelWidget()->winId()); + return; + } + + stopSearch(); + + if(!checkIfModified || checkModified()) + { + if(url.isEmpty()) + { + QString filename; + if ((url = KFileDialog::getOpenURL(currentURL().url(), CatalogImportPlugin::availableImportMimeTypes().join(" ") + ,this)).isEmpty()) + { + return; + } + } + // deactive editor + /*setEnabled(false); + setCursor(KCursor::waitCursor());*/ + + KBabelView *v = viewForURL(url,_project->filename()); + + if(v && v != this) + { + if( newView ) + { + // unregister old catalog + _catalog->removeView(this); + if( !_catalog->hasView() ) + delete _catalog; + + // setup new one + _catalog = v->catalog(); + _catalog->registerView(this); + newFileOpened(_catalog->isReadOnly()); + return; + } + else { + KWin::activateWindow(v->topLevelWidget()->winId()); + return; + } + } + + ConversionStatus stat=_catalog->openURL(url, package); + + switch(stat) + { + case OK: + { + break; + } + case RECOVERED_HEADER_ERROR: // Old name HEADER_ERROR + { + KMessageBox::sorry(this, + i18n("There was an error while reading the file header. " + "Please check the header." )); + editHeader(); + break; + } + case PARSE_ERROR: + { + KMessageBox::error(this + ,i18n("Error while trying to read file:\n %1\n" + "Maybe it is not a valid PO file.").arg(url.prettyURL())); + break; + } + case NO_ENTRY_ERROR: + { + KMessageBox::error(this + ,i18n("Error while reading the file:\n %1\n" + "No entry found.").arg(url.prettyURL())); + break; + } + case RECOVERED_PARSE_ERROR: + { + QString msg=i18n( + "The file contained syntax errors and an attempt has been " + "made to recover it.\n" + "Please check the questionable entries by using " + "Go->Next error"); + KMessageBox::sorry(this,msg); + emitEntryState(); + break; + } + case NO_PERMISSIONS: + { + KMessageBox::error(this,i18n( + "You do not have permissions to read file:\n %1").arg(url.prettyURL())); + break; + } + case NO_FILE: + { + KMessageBox::error(this,i18n( + "You have not specified a valid file:\n %1").arg(url.prettyURL())); + break; + } + case NO_PLUGIN: + { + KMessageBox::error(this,i18n( + "KBabel cannot find a corresponding plugin for the MIME type of the file:\n %1").arg(url.prettyURL())); + break; + } + case UNSUPPORTED_TYPE: + { + KMessageBox::error(this,i18n( + "The import plugin cannot handle this type of the file:\n %1").arg(url.prettyURL())); + break; + } + case STOPPED: + break; + default: + { + KMessageBox::error(this,i18n( + "Error while trying to open file:\n %1").arg(url.prettyURL())); + break; + } + + } + /* + //activate editor + setEnabled(true); + setCursor(KCursor::arrowCursor());*/ + + _autoSaveDelay = _catalog->saveSettings( ).autoSaveDelay; + if ( _autoSaveDelay ) { + if ( !autoSaveTimer ) { + autoSaveTimer = new QTimer( this, "AUTOSAVE TIMER" ); + connect( autoSaveTimer, SIGNAL( timeout( ) ), this, SLOT( slotAutoSaveTimeout( ) ) ); + } + autoSaveTimer->stop( ); + autoSaveTimer->start( 1000 * 60 * _autoSaveDelay ); + } + + } +} + +void KBabelView::revertToSaved() +{ + if(isModified()) + { + // ### TODO: "Cancel" should be the default + if(KMessageBox::warningContinueCancel(this + ,i18n("All changes will be lost if the file " + "is reverted to its last saved state.") + ,i18n("Warning"),i18n("&Revert")) + == KMessageBox::Cancel) + { + return; + } + } + + open(_catalog->currentURL(),QString::null,false); +} + +void KBabelView::openTemplate(const KURL& openURL, const KURL& saveURL) +{ + stopSearch(); + + if(checkModified()) + { + // deactive editor + /*setEnabled(false); + setCursor(KCursor::waitCursor());*/ + + + ConversionStatus stat=_catalog->openURL(openURL,saveURL); + + switch(stat) + { + case OK: + { + break; + } + case RECOVERED_HEADER_ERROR: + { + // For a template, recoverable errors are disqualifying + KMessageBox::sorry(this, + i18n("There was an error while reading the file header of file:\n %1") + .arg(openURL.prettyURL())); + break; + } + case PARSE_ERROR: + { + KMessageBox::error(this + ,i18n("Error while trying to read file:\n %1\n" + "Maybe it is not a valid PO file.").arg(openURL.prettyURL())); + break; + } + case NO_ENTRY_ERROR: + { + KMessageBox::error(this + ,i18n("Error while reading the file:\n %1\n" + "No entry found.").arg(openURL.prettyURL())); + break; + } + case RECOVERED_PARSE_ERROR: + { + // For a template, recoverable errors are disqualifying + KMessageBox::sorry(this, + i18n("Minor syntax errors were found while reading file:\n %1") + .arg(openURL.prettyURL())); + break; + } + case NO_PERMISSIONS: + { + KMessageBox::error(this,i18n("You do not have permissions to read file:\n %1").arg(openURL.prettyURL())); + break; + } + case NO_FILE: + { + KMessageBox::error(this,i18n("You have not specified a valid file:\n %1").arg(openURL.prettyURL())); + break; + } + case NO_PLUGIN: + { + KMessageBox::error(this,i18n( + "KBabel cannot find a corresponding plugin for the MIME type of the file:\n %1").arg(openURL.prettyURL())); + break; + } + case UNSUPPORTED_TYPE: + { + KMessageBox::error(this,i18n( + "The import plugin cannot handle this type of the file:\n %1").arg(openURL.prettyURL())); + break; + } + case STOPPED: + break; + default: + { + KMessageBox::error(this,i18n("Error while trying to open file:\n %1").arg(openURL.prettyURL())); + break; + } + + } + + /* + //activate editor + setEnabled(true); + + setCursor(KCursor::arrowCursor());*/ + } +} + +bool KBabelView::saveFile(bool syntaxCheck) +{ + if(_catalog->isReadOnly()) + { + return saveFileAs(); + } + else + { + ConversionStatus stat=_catalog->saveFile(); + + int whatToDo = -1; + + switch(stat) + { + case OK: + { + informDictionary(); + if(syntaxCheck && _catalog->saveSettings().autoSyntaxCheck ) + return checkSyntax(true,false); + else + return true; + } + case NO_PERMISSIONS: + { + whatToDo=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?").arg(_catalog->currentURL().prettyURL()), + i18n("Error"),KStdGuiItem::save()); + break; + } + case NO_PLUGIN: + { + KMessageBox::error(this,i18n( + "KBabel cannot find a corresponding plugin for the MIME type of file:\n %1").arg(_catalog->currentURL().prettyURL())); + break; + } + case UNSUPPORTED_TYPE: + { + KMessageBox::error(this,i18n( + "The export plugin cannot handle this type of file:\n %1").arg(_catalog->currentURL().prettyURL())); + break; + } + case BUSY: + { + KMessageBox::error(this,i18n( + "KBabel has not finished the last operation yet.\n" + "Please wait.")); + break; + } + case STOPPED: + break; + default: + { + whatToDo=KMessageBox::warningContinueCancel(this, + i18n("An error occurred while trying to write to file:\n%1\n" + "Do you want to save to another file or cancel?").arg(_catalog->currentURL().prettyURL()), + i18n("Error"),KStdGuiItem::save()); + break; + } + } + switch(whatToDo) + { + case KMessageBox::Continue: + return saveFileAs(); + default: + return false; + + } + + } + + return true; +} + +bool KBabelView::saveFileAs(KURL url, bool syntaxCheck) +{ + bool newName=false; + if(url.isEmpty()) + { + if((url = KFileDialog::getSaveURL(currentURL().url(), CatalogExportPlugin::availableExportMimeTypes().join(" "),this)).isEmpty()) + { + return false; + } + newName=true; + } + + if (KIO::NetAccess::exists(url, false, this)) + { + if(KMessageBox::warningContinueCancel(this,QString("<qt>%1</qt>").arg(i18n("The file %1 already exists. " + "Do you want to overwrite it?").arg(url.prettyURL())),i18n("Warning"),i18n("&Overwrite"))==KMessageBox::Cancel) + { + return false; + } + + } + + bool wasReadOnly=_catalog->isReadOnly(); + + ConversionStatus stat=_catalog->saveFileAs(url,true); + + + // if the file was not saved sucessfully ask for saving to another file + if(stat!=OK) + { + bool cancelLoop=false; // flag, if the saving loop should be canceled + do + { + // select the right error message + QString message; + switch(stat) + { + case NO_PERMISSIONS: + { + message=i18n("You do not have permission to write to file:\n%1\n" + "Do you want to save to another file or cancel?").arg(url.prettyURL()); + break; + } + case NO_FILE: + { + message=i18n("You have specified a folder:\n%1\n" + "Do you want to save to another file or cancel?").arg(url.prettyURL()); + break; + } + case NO_PLUGIN: + { + message=i18n("KBabel cannot find a corresponding plugin for the MIME type of the file:\n %1").arg(url.prettyURL()); + break; + } + case UNSUPPORTED_TYPE: + { + message=i18n( + "The export plugin cannot handle this type of the file:\n %1").arg(url.prettyURL()); + break; + } + default: + { + message=i18n("An error occurred while trying to write to file:\n%1\n" + "Do you want to save to another file or cancel?").arg(url.prettyURL()); + break; + } + } + + // now ask, if the user wants to save to another file or cancel + switch(KMessageBox::warningContinueCancel(this,message,i18n("Error"),KStdGuiItem::save())) + { + // save to another file + case KMessageBox::Continue: + { + // ask for new filename + if ((url = KFileDialog::getSaveURL(currentURL().url(), CatalogExportPlugin::availableExportMimeTypes().join(" "), this)).isEmpty()) + { + // if no filename was given cancel all + return false; + } + + if (KIO::NetAccess::exists(url, false, this)) + { + if(KMessageBox::warningContinueCancel(this,i18n("The file %1 already exists.\n" + "Do you want to overwrite it?").arg(url.prettyURL()),i18n("Warning"),i18n("&Overwrite"))==KMessageBox::Continue) + { + stat=_catalog->saveFileAs(url); + if(stat!=OK) + cancelLoop=false; + else + cancelLoop=true; + } + } + else + { + stat=_catalog->saveFileAs(url); + if(stat!=OK) + cancelLoop=false; + else + cancelLoop=true; + } + break; + } + default: // the user do not want to save to another file + return false; + } + } + while(!cancelLoop); + } + + + if(wasReadOnly) + { + msgstrEdit->setReadOnly(false); + } + + emit signalChangeCaption(_catalog->package()); + + if(newName) + { + dictBox->setEditedPackage(_catalog->packageName()); + dictBox->setEditedFile(_catalog->currentURL().url()); + } + + // after save put current edit into dictionary as well + informDictionary(); + + if(syntaxCheck && _catalog->saveSettings().autoSyntaxCheck) + return checkSyntax(true,false); + else + return true; +} + +bool KBabelView::saveFileSpecial() +{ + QString tmpname; + bool result = false; + + { + // generate temporary name + KTempFile tmp; + tmp.setAutoDelete (true); + tmpname = tmp.name (); + } + + { + // make sure the project is freed (pointer going out of scope) + KBabel::Project::Ptr project = KBabel::ProjectManager::open (tmpname); + + project->setSettings( _catalog->saveSettings() ); + KConfigDialog *_prefDialog = new KConfigDialog(this, "project dialog", project->settings(), + KDialogBase::IconList, KDialogBase::Cancel|KDialogBase::Ok|KDialogBase::Help); + + _prefDialog->setCaption( i18n("Special Save Settings") ); + _prefDialog->setHelp("preferences_save"); + + SavePreferences* _prefWidget = new SavePreferences(_prefDialog); + _prefWidget->setAutoSaveVisible(false); + _prefDialog->addPage(_prefWidget, i18n("title of page in preferences dialog","Save") + , "filesave" + , i18n("Options for File Saving")); + + if( _prefDialog->exec() == QDialog::Accepted ) + { + SaveSettings settings = project->saveSettings(); + SaveSettings originalSettings = _catalog->saveSettings(); + _catalog->setSettings(settings); + result = saveFileAs(); + + _catalog->setSettings(originalSettings); + } + } + + QFile::remove( tmpname ); + + return result; +} + +bool KBabelView::checkSyntax() +{ + return checkSyntax(false,false); +} + +bool KBabelView::checkSyntax(bool msgOnlyAtError,bool question) +{ + if(currentURL().isEmpty()) + return false; + + bool returnCode=true; + QString output; + + Msgfmt::Status result=_catalog->checkSyntax( output ); + + const QStringList outputLines = QStringList::split("\n",output); + + switch(result) + { + case Msgfmt::Ok: + { + if(!msgOnlyAtError) + { + KMessageBox::informationList( this, i18n("The file is syntactically correct.\n\n" + "Output of \"msgfmt --statistics\":\n"), outputLines ); + } + returnCode=true; + break; + } + case Msgfmt::Unsupported: + { + if(!msgOnlyAtError) + { + KMessageBox::sorry(this + ,i18n("You can use gettext tools only for checking PO files.")); + } + returnCode=true; + break; + } + case Msgfmt::HeaderError: + case Msgfmt::SyntaxError: + { + QString msg = ( result == Msgfmt::SyntaxError ) + ? i18n("msgfmt detected a syntax error.\n") + : i18n("msgfmt detected a header syntax error.\n"); + + if(question) + { + msg += i18n("\nDo you want to continue or cancel and edit the file again?"); + msg += "\n\n"; + msg += i18n("Output of \"msgfmt --statistics\":\n"); + switch( KMessageBox::warningContinueCancelList( this, msg, + outputLines, i18n("Warning"), KStdGuiItem::cont()) ) + { + case KMessageBox::Continue: + returnCode=true; + break; + default: + returnCode=false; + } + } + else + { +#if KDE_IS_VERSION ( 3, 4, 0 ) + msg += "\n"; + msg += i18n("Please edit the file again."); + msg += "\n\n"; + msg += i18n("Output of \"msgfmt --statistics\":\n"); + KMessageBox::errorList( this, msg, outputLines ); +#else + msg += i18n("Output of \"msgfmt --statistics\":\n"); + msg += output; + msg += "\n\n"; + msg += i18n("Please edit the file again."); + KMessageBox::error( this, msg ); +#endif + returnCode=false; + } + break; + } + case Msgfmt::NoExecutable: + case Msgfmt::Error: + { + QString msg = i18n("While trying to check syntax with msgfmt an error occurred.\n" + "Please make sure that you have installed\n" + "the GNU gettext package properly."); + if(question) + { + msg += i18n("\nDo you want to continue or cancel and edit the file again?"); + switch( KMessageBox::warningContinueCancelList( this, msg, + outputLines, i18n("Warning"), KStdGuiItem::cont() )) + { + case KMessageBox::Continue: + returnCode=true; + break; + default: + returnCode=false; + } + } + else + { +#if KDE_IS_VERSION ( 3, 4, 0 ) + msg += "\n"; + msg += i18n("Please edit the file again."); + KMessageBox::errorList( this, msg, outputLines ); +#else + msg += output; + msg += "\n\n"; + msg += i18n("Please edit the file again."); + KMessageBox::error( this, msg ); +#endif + returnCode=false; + } + + break; + } + } + + emitEntryState(); + + return returnCode; +} + +bool KBabelView::checkAll() +{ + if(currentURL().isEmpty()) + return false; + + bool a,badresult=false; + + QValueList<KDataToolInfo> tools = ToolAction::validationTools(); + + QValueList<KDataToolInfo>::ConstIterator entry = tools.begin(); + for( ; entry != tools.end(); ++entry ) + { + KDataTool* tool = (*entry).createTool(); + if( tool ) + { + a = _catalog->checkUsingTool(tool,entry==tools.begin()); + badresult = badresult || !a; + delete tool; + } + } + + QString output; + + a = (_catalog->checkSyntax(output, false)!=Msgfmt::Ok); + badresult=badresult||a; + + emitEntryState(); + + if(!badresult) + { + KMessageBox::information(this + ,i18n("No mismatch has been found.") + , i18n("Title in Dialog: Perform all checks","Perform All Checks")); + } + else + { + int index=0; + DocPosition pos; + if(!_catalog->hasError(0,pos)) + index = _catalog->nextError(0, pos); + if(index>=0) + { + gotoEntry(pos); + } + + KMessageBox::error(this + ,i18n("Some mismatches have been found.\n" + "Please check the questionable entries by using " + "Go->Next error") + , i18n("Title in Dialog: Perform all checks","Perform All Checks")); + + } + + return !badresult; +} + + + +bool KBabelView::checkModified() +{ + bool flag=true; + + if(isModified()) + { + switch(KMessageBox::warningYesNoCancel(this, + i18n("The document contains unsaved changes.\n" + "Do you want to save your changes or discard them?"),i18n("Warning"), + KStdGuiItem::save(),KStdGuiItem::discard())) + { + case KMessageBox::Yes: + { + flag = saveFile(false); + if(flag && _catalog->saveSettings().autoSyntaxCheck) + { + flag = checkSyntax(true,true); + } + break; + } + case KMessageBox::No: + flag = true; + break; + default: // canceled + flag = false; + break; + } + } + + return flag; +} + +void KBabelView::updateEditor(int form, bool delay) +{ + msgstrEdit->blockSignals(true); + + if(KBabelSettings::autoUnsetFuzzy() && !msgstrEdit->isModified()) + { + disconnect(msgstrEdit,SIGNAL(textChanged()),this,SLOT(autoRemoveFuzzyStatus())); + } + + msgidLabel->setText(_catalog->msgid(_currentIndex), _catalog->msgctxt(_currentIndex)); + msgidLabel->repaint(); + + msgstrEdit->setText(_catalog->msgstr(_currentIndex)); + msgstrEdit->showForm( form ); + msgstrEdit->repaint(); + m_cataloglistview->setSelectedItem(_currentIndex); + + if(KBabelSettings::autoUnsetFuzzy() && _catalog->isFuzzy(_currentIndex)) + { + connect(msgstrEdit,SIGNAL(textChanged()),this,SLOT(autoRemoveFuzzyStatus())); + } + + msgstrEdit->blockSignals(false); + + autoCheck(false); + + // no need to display diff if this message wasn't translated + if(_diffEnabled && !(_catalog->isUntranslated(_currentIndex))) + { + autoDiff(); + } + + if(isActiveWindow() && KBabelSettings::autoSearch() + && !_autoSearchTempDisabled) + { + if (delay) + { + QTimer::singleShot(0, this, SLOT (startSearch())); + } + else + { + startSearch(); + } + } + + msgstrEdit->setFocus(); + msgstrEdit->setModified(false); +} + + +void KBabelView::undo() +{ + if(!_catalog->isUndoAvailable()) + return; + + int newIndex=_catalog->undo(); + + if(newIndex != (int)_currentIndex) + { + DocPosition pos; + pos.item=newIndex; + pos.form=0; + gotoEntry(pos); + } +} + +void KBabelView::redo() +{ + if(!_catalog->isRedoAvailable()) + return; + + int newIndex=_catalog->redo(); + + if(newIndex != (int)_currentIndex) + { + DocPosition pos; + pos.item=newIndex; + pos.form=0; + gotoEntry(pos); + } +} + +void KBabelView::textCut() +{ + if(msgstrEdit->hasFocus()) + { + msgstrEdit->cut(); + } +} + +void KBabelView::textCopy() +{ + if(msgstrEdit->hasSelectedText()) + { + msgstrEdit->copy(); + } + else if(msgidLabel->hasSelectedText()) + { + msgidLabel->copy(); + } + else if(dictBox->isVisible() && dictBox->hasSelectedText()) + { + dictBox->copy(); + } +} + +void KBabelView::textPaste() +{ + msgstrEdit->paste(); +} + + +bool KBabelView::findNext() +{ + if(!_findDialog) + return false; + + if( !_redirectedBackSearch && _findDialog->findOpts().backwards ) + { + _redirectedBackSearch = true; + bool res = findPrev(); + _redirectedBackSearch = false; + return res; + } + + DocPosition pos; + pos.item=_currentIndex; + pos.form=0; + + if( m_commentview->hasFocus() ) { + pos.part = Comment; + pos.offset = m_commentview->currentIndex(); + } + else + if(msgidLabel->hasFocus() ) { + pos.part = Msgid; + pos.offset = msgidLabel->currentIndex(); + } + else { + pos.part = Msgstr; + pos.offset = msgstrEdit->currentIndex(); + pos.form = msgstrEdit->currentForm(); + } + + _findStartPos=pos; + _findBreakAtEnd=false; + + return findNext_internal(pos); +} + +bool KBabelView::findPrev() +{ + if(!_findDialog) + return false; + + if( !_redirectedBackSearch && _findDialog->findOpts().backwards ) + { + _redirectedBackSearch = true; + bool res = findNext(); + _redirectedBackSearch = false; + return res; + } + + DocPosition pos; + pos.item=_currentIndex; + + if( m_commentview->hasFocus()) { + pos.part = Comment; + pos.offset = m_commentview->currentIndex(); + } + else + if(msgidLabel->hasFocus() ) { + pos.part = Msgid; + pos.offset = msgidLabel->currentIndex(); + } + else { + pos.part = Msgstr; + pos.offset = msgstrEdit->currentIndex(); + pos.form = msgstrEdit->currentForm(); + } + + _findStartPos=pos; + _findBreakAtEnd=false; + + return findPrev_internal(pos); +} + + +bool KBabelView::findNext_internal(DocPosition& pos, bool forReplace, bool gui) +{ + FindOptions opts; + if(forReplace) + opts = _replaceDialog->replaceOpts(); + else + opts = _findDialog->findOpts(); + int len=0; + deselectAll(); + + bool success=false; + if( !opts.askFile ) // for find in all files ignore return to the beginning + if(!_findBreakAtEnd && !(success=_catalog->findNext(&opts,pos,len))) { + int r; + if(forReplace) { + _replaceWasAtEnd=true; + _findBreakAtEnd=true; + if(gui) { + r = KMessageBox::questionYesNo(this, + i18n("<qt>%n replacement made.<br>End of document reached.<br>Continue from the beginning?</qt>", + "<qt>%n replacements made.<br>End of document reached.<br>Continue from the beginning?</qt>", + _replacesTotal), QString::null, KStdGuiItem::cont(), KStdGuiItem::cancel()); + } + else { + r = KMessageBox::Yes; + } + } + else { + r = KMessageBox::questionYesNo(this,i18n("End of document reached.\n" + "Continue from the beginning?"), QString::null, KStdGuiItem::cont(), KStdGuiItem::cancel()); + } + if(r == KMessageBox::Yes) { + if(opts.inMsgid && !forReplace) + pos.part = Msgid; + else if(opts.inComment) + pos.part = Comment; + else + pos.part = Msgstr; + + pos.item = 0; + pos.offset = 0; + pos.form = 0; + + } + else + return false; + } + + + if(!success && !_catalog->findNext(&opts,pos,len)) { // reached end the second time + if( !opts.askFile ) + { + if(forReplace) { + KMessageBox::information(this,i18n("%n replacement made","%n replacements made",_replacesTotal)); + } + else { + KMessageBox::information(this,i18n("Search string not found.")); + } + return false; + } + else + { + if( opts.askForNextFile ) + { + int r = KMessageBox::questionYesNo(this,i18n("End of document reached.\n" + "Continue in the next file?"), QString::null, KStdGuiItem::cont(), KStdGuiItem::cancel()); + if( r != KMessageBox::Yes ) return false; + + } + if( isModified() && !opts.askForSave ) saveFile(); + DCOPClient *client = kapp->dcopClient(); + QByteArray data, replyData; + QCString replyType; + bool morefiles = false; // more files to lookup in + if( !client->call( _fileSource,"CatalogManagerIFace", + "findNextFile()", data, replyType, replyData) ) kdDebug(KBABEL) << "unable to call, reply type is " << replyType << endl; + else if( replyType == "QCString" ) + { + QDataStream rep( replyData, IO_ReadOnly); + QCString f; + rep >> f; + QString foundFile = QString::fromUtf8(f); + morefiles = !f.isEmpty() && !f.isNull(); + if( morefiles ) + { + emit open( KURL(foundFile) ); + pos.item = 0; + pos.part = Msgid; + pos.offset = 0; + _catalog->findNext(&opts,pos,len); + } else { + if( f.isNull() ) // is there possibility to get a new file? + { + // no, this is the end finally + if( forReplace ) + KMessageBox::information(this,i18n("%n replacement made","%n replacements made",_replacesTotal)); + else + KMessageBox::information(this,i18n("Search string not found.")); + return false; + } + else + { + // there can be a new file, let user know + showTryLaterMessageBox(); + if( forReplace ) return true; // let replace dialog stay + else return false; + } + } + } else { + KMessageBox::error(this,i18n("DCOP communication with Catalog Manager failed.")); + return false; + } + } + } + if(gui) { + if( _currentIndex != pos.item + || (pos.part==Msgstr && msgstrEdit->currentForm() != pos.form) ) + { + gotoEntry(pos); + } + + int line,col,endline,endcol; + switch(pos.part) { + case Msgid: + msgidLabel->selectAll(false); + msgidLabel->setFocus(); + msgidLabel->offset2Pos(pos.offset,line,col); + msgidLabel->offset2Pos(pos.offset+len,endline,endcol); + + msgidLabel->setSelection(line,col,endline,endcol); + msgidLabel->setCursorPosition(endline,endcol); + + _lastFoundString=msgidLabel->selectedText(); + break; + case Msgstr: + msgstrEdit->selectAll(false); + msgstrEdit->setFocus(); + msgstrEdit->offset2Pos(pos.offset,line,col); + msgstrEdit->offset2Pos(pos.offset+len,endline,endcol); + + msgstrEdit->setSelection(line,col,endline,endcol); + msgstrEdit->setCursorPosition(endline,endcol); + + _lastFoundString=msgstrEdit->selectedText(); + break; + case Comment: + { + m_mainwindow->makeWidgetDockVisible (m_commentview); + _lastFoundString= m_commentview->selectText (pos.offset, pos.offset+len); + break; + } + case UndefPart: + break; + } + } + + if(forReplace) { + kdDebug(KBABEL) << "This is forReplace" << endl; + _replaceLen=len; + + bool finished=false; + // check if we had reached the beginning before and now are before our starting position + if(_replaceWasAtEnd) { + if( pos.item > _findStartPos.item ) { + finished=true; + } + else if(pos.item == _findStartPos.item) { + if(pos.part==Msgstr && !opts.inComment && pos.offset >= _findStartPos.offset+_replaceExtraOffset) + finished=true; + else if(pos.part==Comment && pos.offset >= _findStartPos.offset+_replaceExtraOffset) + finished=true; + } + } + + if(finished) { + KMessageBox::information(this,i18n("%n replacement made","%n replacements made",_replacesTotal)); + return false; + } + + } + + return true; +} + +bool KBabelView::findPrev_internal(DocPosition& pos, bool forReplace, bool gui) +{ + FindOptions opts; + if(forReplace) + opts = _replaceDialog->replaceOpts(); + else + opts = _findDialog->findOpts(); + + int len=0; + + deselectAll(); + bool success=false; + if(!_findBreakAtEnd && !(success=_catalog->findPrev(&opts,pos,len))) { + int r; + + if(forReplace) { + _replaceWasAtEnd=true; + _findBreakAtEnd=true; + if(gui) { + r = KMessageBox::questionYesNo(this, + i18n("<qt>%n replacement made.<br>Beginning of document reached.<br>Continue from the end?</qt>", + "<qt>%n replacements made.<br>Beginning of document reached.<br>Continue from the end?</qt>", + _replacesTotal),QString::null, KStdGuiItem::cont(), KStdGuiItem::cancel()); + } + else { + r = KMessageBox::Yes; + } + + } + else { + r = KMessageBox::questionYesNo(this,i18n("Beginning of document reached.\n" + "Continue from the end?"), QString::null, KStdGuiItem::cont(), KStdGuiItem::cancel()); + } + if(r == KMessageBox::Yes) { + pos.item = _catalog->numberOfEntries()-1; + + if(opts.inComment) { + pos.part = Comment; + pos.offset = _catalog->comment(pos.item).length(); + } + else if(opts.inMsgstr || forReplace) { + pos.part = Msgstr; + if( _catalog->msgstr(pos.item).empty() ) pos.offset=0; + else pos.offset = _catalog->msgstr(pos.item).last().length(); + pos.form = _catalog->msgstr(pos.item).count()-1; + } + else { + pos.part = Msgid; + //FIXME: we should search in msgid plural forms + pos.offset = _catalog->msgid(pos.item).first().length(); + } + } + else + return false; + } + + // start at the end + if(!success && !_catalog->findPrev(&opts,pos,len)) { + if(forReplace) { + KMessageBox::information(this,i18n("%n replacement made","%n replacements made",_replacesTotal)); + } + else { + KMessageBox::information(this,i18n("Search string not found.")); + } + return false; + } + else if(gui) { + if(_currentIndex != pos.item + || (pos.part==Msgstr && msgstrEdit->currentForm() != pos.form) ) + gotoEntry(pos); + + int line,col,endline,endcol; + switch(pos.part) { + case Msgid: + msgidLabel->selectAll(false); + msgidLabel->setFocus(); + msgidLabel->offset2Pos(pos.offset+len,line,col); + msgidLabel->offset2Pos(pos.offset,endline,endcol); + + msgidLabel->setSelection(line,col,endline,endcol); + msgidLabel->setCursorPosition(endline,endcol); + + _lastFoundString=msgidLabel->selectedText(); + break; + case Msgstr: + msgstrEdit->selectAll(false); + msgstrEdit->setFocus(); + msgstrEdit->offset2Pos(pos.offset+len,line,col); + msgstrEdit->offset2Pos(pos.offset,endline,endcol); + + msgstrEdit->setSelection(line,col,endline,endcol); + msgstrEdit->setCursorPosition(endline,endcol); + + _lastFoundString=msgstrEdit->selectedText(); + break; + case Comment: + m_mainwindow->makeWidgetDockVisible (m_commentview); + _lastFoundString=m_commentview-> + selectText(pos.offset, pos.offset+len); + break; + case UndefPart: + break; + } + } + + if(forReplace) { + _replaceLen=len; + + bool finished=false; + // check if we had reached the beginning before and now are before our starting position + if(_replaceWasAtEnd) { + if( pos.item < _findStartPos.item ) { + finished=true; + } + else if(pos.item == _findStartPos.item) { + if(pos.part==Comment && !opts.inMsgstr && pos.offset < _findStartPos.offset+_replaceExtraOffset) + finished=true; + else if(pos.part==Msgstr && pos.offset < _findStartPos.offset+_replaceExtraOffset) + finished=true; + } + } + + if(finished) { + KMessageBox::information(this,i18n("%n replacement made","%n replacements made",_replacesTotal)); + return false; + } + + } + + + return true; +} + + +void KBabelView::find() +{ + Part hadFocus; + if(msgidLabel->hasFocus()) + hadFocus=Msgid; + else if(m_commentview->hasFocus()) + hadFocus=Comment; + else + hadFocus=Msgstr; + + if( !_findDialog ) { + _findDialog = new FindDialog(false,this); + } + + QString marked; + if(msgstrEdit->hasFocus()) { + marked=msgstrEdit->selectedText(); + msgstrEdit->selectAll(false); + } + else if(m_commentview->hasFocus()) { + marked=m_commentview->selectedText(); + m_commentview->textDeselectAll(); + } + else if(msgidLabel->hasFocus()) { + marked=msgidLabel->selectedText(); + msgidLabel->selectAll(false); + } + + + if(marked==_lastFoundString) + marked=""; + + if( _findDialog->exec(marked) == QDialog::Accepted ) { + DocPosition pos; + FindOptions opts=_findDialog->findOpts(); + opts.askFile = false; // do not search in more files + _findDialog->setFindOpts(opts); + + if(opts.fromCursor) { + _findBreakAtEnd=false; + pos.item=_currentIndex; + + if(hadFocus == Comment && opts.inComment) { + pos.part = Comment; + pos.offset = m_commentview->currentIndex(); + } + else + if( hadFocus == Msgid && opts.inMsgid) { + pos.part = Msgid; + pos.offset = msgidLabel->currentIndex(); + } + else { + pos.part = Msgstr; + pos.offset = msgstrEdit->currentIndex(); + } + } + else { + _findBreakAtEnd=true; + + if(opts.backwards) { + pos.item=_catalog->numberOfEntries(); + if(opts.inComment) + pos.part=Comment; + else if(opts.inMsgstr) + pos.part=Msgstr; + else + pos.part=Msgid; + + pos.offset=1000; // set to a high number + } + else { + pos.item=0; + if(opts.inMsgid) + pos.part=Msgid; + else if(opts.inMsgstr) + pos.part=Msgstr; + else + pos.part=Comment; + + pos.offset=0; + } + } + + if( opts.backwards ) { + _findStartPos=pos; + findPrev_internal(pos); + } + else { + _findStartPos=pos; + findNext_internal(pos); + } + } +} + +void KBabelView::replace() +{ + _replacesTotal=0; + _replaceLen=0; + _replaceWasAtEnd=false; + _replaceExtraOffset=0; + + Part hadFocus; + if(msgidLabel->hasFocus()) + hadFocus=Msgid; + else if(m_commentview->hasFocus()) + hadFocus=Comment; + else + hadFocus=Msgstr; + + if( !_replaceDialog ) { + _replaceDialog = new FindDialog(true,this); + } + QString marked; + if(msgstrEdit->hasFocus()) { + marked=msgstrEdit->selectedText(); + msgstrEdit->selectAll(false); + } + else if(m_commentview->hasFocus()) { + marked=m_commentview->selectedText(); + m_commentview->textDeselectAll(); + } + else if(msgidLabel->hasFocus()) { + marked=msgidLabel->selectedText(); + msgidLabel->selectAll(false); + } + + if(marked==_lastFoundString) + marked=""; + + if( _replaceDialog->exec(marked) == QDialog::Accepted ) { + KBabel::ReplaceOptions opts=_replaceDialog->replaceOpts(); + if(opts.fromCursor) { + _findBreakAtEnd=false; + _replacePos.item=_currentIndex; + + if(hadFocus==Comment && opts.inComment) { + _replacePos.part = Comment; + _replacePos.offset = m_commentview->currentIndex(); + } + else + { + _replacePos.part = Msgstr; + _replacePos.offset = msgstrEdit->currentIndex(); + } + } + else { + _findBreakAtEnd=true; + if(opts.backwards) { + _replacePos.item=_catalog->numberOfEntries(); + if(opts.inComment) + _replacePos.part=Comment; + else + _replacePos.part=Msgstr; + + _replacePos.offset=1000; // set to a high number + } + else { + _replacePos.item=0; + if(opts.inMsgstr) + _replacePos.part=Msgstr; + else + _replacePos.part=Comment; + + _replacePos.offset=0; + } + } + + // do not ask for next file from catalog manager + opts.askFile = false; + _replaceDialog->setReplaceOpts(opts); + + bool success; + + if( opts.backwards ) { + _findStartPos=_replacePos; + success = findPrev_internal(_replacePos,true,opts.ask); + } + else { + _findStartPos=_replacePos; + success = findNext_internal(_replacePos,true,opts.ask); + } + + + + if(success) { + if(!_replaceAskDialog) { + _replaceAskDialog = new ReplaceDialog(this); + connect(_replaceAskDialog,SIGNAL(replace()),this,SLOT(replaceNext())); + connect(_replaceAskDialog,SIGNAL(next()),this,SLOT(findNextReplace())); + connect(_replaceAskDialog,SIGNAL(replaceAll()),this,SLOT(replaceAll())); + } + + if(opts.ask) { + _replaceAskDialog->exec(); + } + else + replaceAll(); + } + + } +} + + +void KBabelView::replaceNext() +{ + _replacesTotal++; + + KBabel::ReplaceOptions opts=_replaceDialog->replaceOpts(); + + // calculate the diff to original offset due to replacing a string + // in the starting item + if(_findStartPos.item == _replacePos.item ) { + if((opts.backwards && !_replaceWasAtEnd) + || (!opts.backwards && _replaceWasAtEnd)) { + _replaceExtraOffset += (opts.replaceStr.length()-_replaceLen); + } + } + + Part part; + uint form = 0; + QString str; + + if(_replacePos.part==Msgstr) { + part=Msgstr; + str = _catalog->msgstr(_replacePos.item).first() + .mid(_replacePos.offset,_replaceLen); + form=_replacePos.form; + } + else if(_replacePos.part==Comment) { + part = Comment; + str = _catalog->comment(_replacePos.item) + .mid(_replacePos.offset,_replaceLen); + } + else { + kdWarning() << "msgid can not be changed in KBabelView::replaceNext()" + << endl; + return; +} + + _catalog->applyBeginCommand( _replacePos.item,part,this); + + DelTextCmd* delCmd = new DelTextCmd(_replacePos.offset,str,form); + delCmd->setPart(part); + delCmd->setIndex(_replacePos.item); + _catalog->applyEditCommand(delCmd,0); + + InsTextCmd* insCmd = new InsTextCmd(_replacePos.offset,opts.replaceStr,form); + insCmd->setPart(part); + insCmd->setIndex(_replacePos.item); + _catalog->applyEditCommand(insCmd,0); + + _catalog->applyEndCommand( _replacePos.item,part,this); + + // now find next string + bool success; + + if( opts.backwards ) { + success = findPrev_internal(_replacePos,true); + } + else { + _replacePos.offset+=opts.replaceStr.length(); + success = findNext_internal(_replacePos,true); + } + + if(!success) { + if(_replaceAskDialog && _replaceAskDialog->isVisible()) + _replaceAskDialog->hide(); + } + +} + + +void KBabelView::replaceAll() +{ + if(_replaceAskDialog && _replaceAskDialog->isVisible()) + _replaceAskDialog->hide(); + + KBabel::ReplaceOptions opts=_replaceDialog->replaceOpts(); + bool success=true; + + _catalog->applyBeginCommand(_replacePos.item,Msgstr,this); + + while(success) + { + kapp->processEvents(100); + + _replacesTotal++; + + // calculate the diff to original offset due to replacing a string + // in the starting item + if(_findStartPos.item == _replacePos.item ) { + if((opts.backwards && !_replaceWasAtEnd) || (!opts.backwards && _replaceWasAtEnd)) { + _replaceExtraOffset += (opts.replaceStr.length()-_replaceLen); + } + } + + Part part; + uint form=0; + QString str; + + if(_replacePos.part==Msgstr) { + part=Msgstr; + form=_replacePos.form; + str = _catalog->msgstr(_replacePos.item).first().mid(_replacePos.offset,_replaceLen); + } + else if(_replacePos.part==Comment) { + part = Comment; + str = _catalog->comment(_replacePos.item).mid(_replacePos.offset,_replaceLen); + } + else { + kdWarning() << "msgid can not be changed in KBabelView::replaceNext()" << endl; + return; + } + + DelTextCmd* delCmd = new DelTextCmd(_replacePos.offset,str,form); + delCmd->setPart(part); + delCmd->setIndex(_replacePos.item); + _catalog->applyEditCommand(delCmd,0); + + InsTextCmd* insCmd = new InsTextCmd(_replacePos.offset,opts.replaceStr,form); + insCmd->setPart(part); + insCmd->setIndex(_replacePos.item); + _catalog->applyEditCommand(insCmd,0); + + + // now find next string + if( opts.backwards ) { + success = findPrev_internal(_replacePos,true,false); + } + else { + _replacePos.offset+=opts.replaceStr.length(); + success = findNext_internal(_replacePos,true,false); + } + } + + _catalog->applyEndCommand(_replacePos.item,Msgstr,this); +} + +void KBabelView::findNextReplace() +{ + bool success; + KBabel::ReplaceOptions opts=_replaceDialog->replaceOpts(); + + if( opts.backwards ) { + success = findPrev_internal(_replacePos,true); + } + else { + _replacePos.offset++; + success = findNext_internal(_replacePos,true); + } + + if(!success) { + if(_replaceAskDialog && _replaceAskDialog->isVisible()) + _replaceAskDialog->hide(); + } + +} + +void KBabelView::findInFile(QCString fileSource, FindOptions options) +{ + DocPosition pos; + pos.item=0; + pos.part=Msgid; + pos.offset=0; + _findStartPos=pos; + _findBreakAtEnd=true; // do not start from the beginning at the end + _showTryLaterBox=true; + + // delete dontDisplayAgain from configuration + KConfig* config = KGlobal::config(); + KConfigGroupSaver saver(config,"Notification Messages"); + config->writeEntry("waitForNextFile",true); + + // set that there can be more files + options.askFile = true; + + _fileSource = fileSource; + + if( !_findDialog ) _findDialog = new FindDialog(false,this); + _findDialog->setFindOpts(options); + findNext_internal(pos); +} + +void KBabelView::replaceInFile(QCString fileSource, KBabel::ReplaceOptions options) +{ + _replacePos.item=0; + _replacePos.part=Msgid; + _replacePos.offset=0; + + _replacesTotal=0; + _replaceLen=0; + _replaceWasAtEnd=false; + _replaceExtraOffset=0; + + _findBreakAtEnd=true; + _showTryLaterBox=true; + + // delete dontDisplayAgain from configuration + KConfig* config = KGlobal::config(); + KConfigGroupSaver saver(config,"Notification Messages"); + config->writeEntry("waitForNextFile",true); + + // set that there can be more files + options.askFile = true; + + _fileSource = fileSource; + + if( !_replaceDialog ) _replaceDialog = new FindDialog(true,this); + _replaceDialog->setReplaceOpts(options); + + bool success; + + success = findNext_internal(_replacePos,true); + + if(!success) kdDebug(KBABEL) << "Not found in file where catalog manager told us. This should not happen" << endl; + else { + if(!_replaceAskDialog) { + _replaceAskDialog = new ReplaceDialog(this); + connect(_replaceAskDialog,SIGNAL(replace()),this,SLOT(replaceNext())); + connect(_replaceAskDialog,SIGNAL(next()),this,SLOT(findNextReplace())); + connect(_replaceAskDialog,SIGNAL(replaceAll()),this,SLOT(replaceAll())); + } + + if(options.ask) { + _replaceAskDialog->exec(); + } + else replaceAll(); + } +} + + +void KBabelView::deselectAll() +{ + msgstrEdit->selectAll(false); +// FIXME: commentEdit->selectAll(false); + msgidLabel->selectAll(false); +} + +void KBabelView::selectAll() +{ + if(msgstrEdit->hasFocus()) + { + msgstrEdit->selectAll(); + } +// FIXME: else if(commentEdit->hasFocus()) +// FIXME: { +// FIXME: commentEdit->selectAll(); +// FIXME: } + else if(msgidLabel->hasFocus()) + { + msgidLabel->selectAll(); + } +} + +void KBabelView::clear() +{ + if(msgstrEdit->hasFocus()) + { + msgstrEdit->clear(); + } +// FIXME: else if(commentEdit->hasFocus()) +// FIXME: { +// FIXME: commentEdit->clear(); +// FIXME: } +} + +void KBabelView::msgid2msgstr() +{ + // FIXME: should care about plural forms + QString text = _catalog->msgid(_currentIndex).first(); + + // this is KDE specific: + if(text.find("_: NAME OF TRANSLATORS\\n")==0) + { + text=_catalog->identitySettings().authorLocalizedName; + } + else if(text.find("_: EMAIL OF TRANSLATORS\\n")==0) + { + text=_catalog->identitySettings().authorEmail; + } + else if(_catalog->isGeneratedFromDocbook() && text.find("ROLES_OF_TRANSLATORS")==0) + { + text="<othercredit role=\\\"translator\\\">\n" + "<firstname></firstname><surname></surname>\n" + "<affiliation><address><email>"+_catalog->identitySettings().authorEmail+"</email></address>\n" + "</affiliation><contrib></contrib></othercredit>"; + } + else if(_catalog->isGeneratedFromDocbook() && text.find("CREDIT_FOR_TRANSLATORS")==0) + { + text="<para>"+_catalog->identitySettings().authorLocalizedName+"\n"+ + "<email>"+_catalog->identitySettings().authorEmail+"</email></para>"; + } + else if(text.contains(_catalog->miscSettings().singularPlural)) + { + text.replace(_catalog->miscSettings().singularPlural,""); + } + // end of KDE specific part + + + QRegExp reg=_catalog->miscSettings().contextInfo; + if(text.contains(reg)) + { + text.replace(reg,""); + } + + modifyMsgstrText(0,text,true); + + msgstrEdit->setCursorPosition(0,0); +} + +void KBabelView::search2msgstr() +{ + modifyMsgstrText(0,dictBox->translation(),true); + + msgstrEdit->setCursorPosition(0,0); +} + + +void KBabelView::gotoFirst() +{ + DocPosition pos; + pos.item=0; + pos.form=0; + gotoEntry(pos); +} + +void KBabelView::gotoLast() +{ + DocPosition pos; + pos.item=_catalog->numberOfEntries()-1; + pos.form=0; + gotoEntry(pos); +} + +void KBabelView::gotoNext() +{ + DocPosition pos; + pos.item=_currentIndex; + pos.form=msgstrEdit->currentForm(); + + if( (int)pos.form+1 < _catalog->defaultNumberOfPluralForms() + && _catalog->pluralForm(_currentIndex)==Gettext ) + { + pos.form++; + } + else + { + // check, if we are already showing the last entry + if(_currentIndex>=_catalog->numberOfEntries()-1) + { + return; + } + else + { + pos.item++; + pos.form=0; + } + } + + gotoEntry(pos); +} + +void KBabelView::gotoPrev() +{ + DocPosition pos; + pos.item=_currentIndex; + pos.form=msgstrEdit->currentForm(); + // check, if we are already showing the first entry + if(_currentIndex==0 && pos.form==0) + { + return; + } + else + { + if( pos.form==0 ) + { + pos.item--; + if( _catalog->pluralForm(pos.item)==Gettext ) + pos.form=_catalog->defaultNumberOfPluralForms()-1; + } else pos.form--; + + gotoEntry(pos); + } + +} + +void KBabelView::gotoEntry() +{ + if( !_gotoDialog ) + { + _gotoDialog = new GotoDialog(_catalog->numberOfEntries(),this); + } + + _gotoDialog->exec(); + if( _gotoDialog->result() ) + { + int number=_gotoDialog->number()-1; + int max=_catalog->numberOfEntries()-1; + + if(number>max) + number=max; + else + if(number<0) + number=0; + + DocPosition pos; + pos.item=number; + pos.form=0; + gotoEntry(pos); + } + +} + +void KBabelView::gotoNextFuzzy() +{ + DocPosition pos; + if( _catalog->nextFuzzy(_currentIndex,pos) >= 0 ) + { + gotoEntry(pos); + } +} + +void KBabelView::gotoPrevFuzzy() +{ + DocPosition pos; + if( _catalog->prevFuzzy(_currentIndex,pos) >= 0 ) + { + gotoEntry(pos); + } +} + +void KBabelView::gotoNextError() +{ + DocPosition pos; + if(_catalog->nextError(_currentIndex,pos) >= 0 ) + { + _dontBeep=true; + gotoEntry(pos); + _dontBeep=false; + } +} + +void KBabelView::gotoPrevError() +{ + DocPosition pos; + if(_catalog->prevError(_currentIndex,pos) >= 0) + { + _dontBeep=true; + gotoEntry(pos); + _dontBeep=false; + } +} + +void KBabelView::gotoNextFuzzyOrUntrans() +{ + DocPosition fuzzyPos,untransPos; + + int fuzzyIndex=_catalog->nextFuzzy(_currentIndex,fuzzyPos); + int untransIndex=_catalog->nextUntranslated(_currentIndex,untransPos); + + if(fuzzyIndex<0 && untransIndex<0 ) return; + + if(fuzzyIndex<0) + { + gotoEntry(untransPos); + return; + } + if(untransIndex<0) + { + gotoEntry(fuzzyPos); + return; + } + + if( fuzzyIndex<untransIndex + || (fuzzyIndex==untransIndex && fuzzyPos.form<untransPos.form)) + { + gotoEntry(fuzzyPos); + } + else + { + gotoEntry(untransPos); + } +} + +void KBabelView::gotoPrevFuzzyOrUntrans() +{ + DocPosition fuzzyPos,untransPos; + + int fuzzyIndex=_catalog->prevFuzzy(_currentIndex,fuzzyPos); + int untransIndex=_catalog->prevUntranslated(_currentIndex,untransPos); + + if(fuzzyIndex<0 && untransIndex<0 ) return; + + if(fuzzyIndex<0) + { + gotoEntry(untransPos); + return; + } + if(untransIndex<0) + { + gotoEntry(fuzzyPos); + return; + } + + if( fuzzyIndex>untransIndex + || (fuzzyIndex==untransIndex && fuzzyPos.form>untransPos.form)) + { + gotoEntry(fuzzyPos); + } + else + { + gotoEntry(untransPos); + } +} + +void KBabelView::gotoNextUntranslated() +{ + DocPosition pos; + if(_catalog->nextUntranslated(_currentIndex,pos)>=0) + { + gotoEntry(pos); + } +} + +void KBabelView::gotoPrevUntranslated() +{ + DocPosition pos; + if(_catalog->prevUntranslated(_currentIndex,pos)>=0) + { + gotoEntry(pos); + } +} + + +void KBabelView::gotoEntry(const DocPosition& pos, bool updateHistory) +{ + // clear up statusbar + emit signalChangeStatusbar(""); + + if(updateHistory) + { + if(_forwardHistory.count()>0) + { + emit signalForwardHistory(false); + } + _forwardHistory.clear(); + _backHistory.append(_currentIndex); + + if(_backHistory.count()==1) + { + emit signalBackHistory(true); + } + else if(_backHistory.count()>MAX_HISTORY) + { + _backHistory.remove(_backHistory.begin()); + } + } + + informDictionary(); + + _currentPos=pos; + _currentIndex=pos.item; + updateEditor(pos.form,true); + emitEntryState(); + updateTags(); + updateArgs(); +} + +void KBabelView::msgstrPluralFormChanged ( uint index ) +{ + _currentPos.form = index; + emit signalDisplayed (_currentPos); +} + +void KBabelView::backHistory() +{ + if(_backHistory.isEmpty()) + { + kdDebug(KBABEL) << "KBabelView::backHistory called without any history." << endl; + return; + } + + _forwardHistory.append(_currentIndex); + uint index=_backHistory.last(); + _backHistory.remove(_backHistory.fromLast()); + + DocPosition pos; + pos.item=index; + pos.form=0; + gotoEntry(pos,false); + + if(_backHistory.count()==0) + { + emit signalBackHistory(false); + } + if(_forwardHistory.count()==1) + { + emit signalForwardHistory(true); + } +} + +void KBabelView::forwardHistory() +{ + if(_forwardHistory.isEmpty()) + { + kdDebug(KBABEL) << "KBabelView::forwardHistory called without any history." << endl; + return; + } + + _backHistory.append(_currentIndex); + uint index=_forwardHistory.last(); + _forwardHistory.remove(_forwardHistory.fromLast()); + + DocPosition pos; + pos.item=index; + pos.form=0; + gotoEntry(pos,false); + + if(_forwardHistory.count()==0) + { + emit signalForwardHistory(false); + } + if(_backHistory.count()==1) + { + emit signalBackHistory(true); + } +} + +void KBabelView::removeFuzzyStatus() +{ + bool newState = !_catalog->isFuzzy(_currentIndex); + _catalog->setFuzzy(_currentIndex,newState); + + // ensure we will update the translation memory as needed + msgstrEdit->setModified (true); + emit signalFuzzyDisplayed(newState); +} + + +void KBabelView::editHeader() +{ + HeaderEditor* editor=_catalog->headerEditor(); + + int editHeight=editor->height(); + int editWidth=editor->width(); + int w=width(); + int h=height(); + + int x=w/2-editWidth/2; + int y=h/2-editHeight/2; + + editor->move(mapToGlobal(QPoint(x,y))); + + editor->show(); + editor->raise(); + +} + + +void KBabelView::stopSearch() +{ + dictBox->stopSearch(); +} + +void KBabelView::startSearch() +{ + startSearch(false); +} + +void KBabelView::startSearch(bool delay) +{ + QString msg = _catalog->msgid(_currentIndex,true).first(); + QRegExp reg=_catalog->miscSettings().contextInfo; + if(msg.contains(reg)) + { + msg.replace(reg,""); + } + + dictBox->setActiveModule(KBabelSettings::defaultModule()); + if(delay) + { + dictBox->startDelayedSearch(msg); + } + else + { + dictBox->startSearch(msg); + } +} + +void KBabelView::startSearch(const QString module) +{ + // FIXME: should care about plural forms + QString msg = _catalog->msgid(_currentIndex,true).first(); + QRegExp reg=_catalog->miscSettings().contextInfo; + if(msg.contains(reg)) + { + msg.replace(reg,""); + } + + dictBox->setActiveModule(module); + dictBox->startSearch(msg); + +} + +void KBabelView::startSelectionSearch() +{ + startSelectionSearch(KBabelSettings::defaultModule()); +} + +void KBabelView::startSelectionSearch(const QString module) +{ + dictBox->setActiveModule(module); + + if(msgidLabel->hasSelectedText()) + { + // TODO: should we care about end of lines? + dictBox->startSearch(msgidLabel->selectedText()); + } + else if(msgstrEdit->hasSelectedText()) + { + dictBox->startTranslationSearch(msgstrEdit->selectedText()); + } + else + { + // should care about plural forms + QString msg = _catalog->msgid(_currentIndex,true).first(); + QRegExp reg=_catalog->miscSettings().contextInfo; + if(msg.contains(reg)) + { + msg.replace(reg,""); + } + + dictBox->startSearch(msg); + } +} + + +void KBabelView::emitEntryState() +{ + // flag, so I don't have to always change the color of the text + static bool isError=false; + + emit signalDisplayed(_currentPos); + + emit signalFirstDisplayed(_currentIndex==0, msgstrEdit->currentForm()==0); + emit signalLastDisplayed((unsigned)(_currentIndex+1)==_catalog->numberOfEntries(), (signed)(msgstrEdit->currentForm())==_catalog->numberOfPluralForms(_currentIndex)-1); + + bool fuzzy=_catalog->isFuzzy(_currentIndex); + bool untrans=_catalog->isUntranslated(_currentIndex); + emit signalFuzzyDisplayed(fuzzy); + emit signalUntranslatedDisplayed(untrans); + emit signalFuzzyAfterwards(_catalog->hasFuzzyAfterwards(_currentIndex)); + emit signalUntranslatedAfterwards(_catalog->hasUntranslatedAfterwards(_currentIndex)); + emit signalFuzzyInFront(_catalog->hasFuzzyInFront(_currentIndex)); + emit signalUntranslatedInFront(_catalog->hasUntranslatedInFront(_currentIndex)); + + emit signalErrorAfterwards(_catalog->hasErrorAfterwards(_currentIndex)); + emit signalErrorInFront(_catalog->hasErrorInFront(_currentIndex)); + + DocPosition pos; + if( _catalog->hasError(_currentIndex,pos) != isError ) + { + isError = !isError; + + emit signalFaultyDisplayed(isError); + + if(isError) + { + QPalette palette=msgstrEdit->palette(); + palette.setColor( QColorGroup::Text, red ); + + if( _catalog->itemStatus(_currentIndex).contains("syntax error")) + { + msgstrEdit->setCurrentColor( MsgMultiLineEdit::ErrorColor ); + } + else + if( !_catalog->itemStatus(_currentIndex).isEmpty() && KBabelSettings::autoCheckColorError()) + { + msgstrEdit->setCurrentColor( MsgMultiLineEdit::ErrorColor ); + } + } + else + { + msgstrEdit->setCurrentColor( MsgMultiLineEdit::NormalColor ); + } + } + +} + +void KBabelView::checkFuzzies() +{ + emit signalFuzzyAfterwards(_catalog->hasFuzzyAfterwards(_currentIndex)); + emit signalFuzzyInFront(_catalog->hasFuzzyInFront(_currentIndex)); +} + +void KBabelView::checkUntranslated() +{ + emit signalUntranslatedAfterwards(_catalog->hasUntranslatedAfterwards(_currentIndex)); + emit signalUntranslatedInFront(_catalog->hasUntranslatedInFront(_currentIndex)); +} + +void KBabelView::autoRemoveFuzzyStatus() +{ + // only at first text change remove fuzzy status + disconnect(msgstrEdit,SIGNAL(textChanged()),this,SLOT(autoRemoveFuzzyStatus())); + + //removeFuzzyStatus(); +} + +void KBabelView::toggleFuzzyLed(bool on) +{ + if(!_fuzzyLed) + return; + + if(on) + { + if(_fuzzyLed->state()==KLed::Off) + { + _fuzzyLed->on(); + } + } + else + { + if(_fuzzyLed->state()==KLed::On) + _fuzzyLed->off(); + } +} + +void KBabelView::toggleUntransLed(bool on) +{ + if(!_untransLed) + return; + + if(on) + { + if(_untransLed->state()==KLed::Off) + _untransLed->on(); + } + else + { + if(_untransLed->state()==KLed::On) + _untransLed->off(); + } +} + +void KBabelView::toggleErrorLed(bool on) +{ + if(!_errorLed) + return; + + if(on) + { + if(_errorLed->state()==KLed::Off) + { + _errorLed->on(); + } + } + else + { + if(_errorLed->state()==KLed::On) + _errorLed->off(); + } +} + + + + +void KBabelView::showError(const QString& message) +{ + KMessageBox::error(this,message); +} + +void KBabelView::dragEnterEvent(QDragEnterEvent *event) +{ + // accept uri drops only + event->accept(KURLDrag::canDecode(event)); +} + +void KBabelView::dropEvent(QDropEvent *event) +{ + KURL::List uri; + + // see if we can decode a URI.. if not, just ignore it + if (KURLDrag::decode(event, uri)) + { + processUriDrop(uri , mapToGlobal(event->pos())); + } +} + + +bool KBabelView::eventFilter( QObject* object, QEvent* event) +{ + if(event->type() == QEvent::DragEnter) + { + QDragEnterEvent* e = (QDragEnterEvent*) event; + if(KURLDrag::canDecode(e)) + { + e->accept(true); + return true; + } + } + else if(event->type() == QEvent::Drop) + { + KURL::List uri; + // see if we can decode a URI.. if not, just ignore it + if (KURLDrag::decode((QDropEvent*)event, uri)) + { + processUriDrop(uri ,( (QWidget*)object)->mapToGlobal( ( (QDropEvent*)event )->pos())); + return true; + } + } + else if(event->type() == QEvent::KeyPress) + { + // This is a little workaround to use CTRL+ALT+Home, CTRL+ALT+End, Undo keys + // to go to the first and the last entry. Because otherwise + // CTRL+Home and CTRL+End and Undo are caught by QTextEdit + QKeyEvent *ke = (QKeyEvent*)event; + + if(ke->key() == Key_Home && ke->state() == (AltButton | ControlButton)) + { + gotoFirst(); + return true; + } + else if(ke->key() == Key_End + && ke->state() == (AltButton | ControlButton)) + { + gotoLast(); + return true; + } + else if( KShortcut(KKey(ke)) == KStdAccel::undo() ) + { + undo(); + return true; + } + else if( KShortcut(KKey(ke)) == KStdAccel::redo() ) + { + redo(); + return true; + } + else if( ke->key() == Key_Insert ) + { + m_mainwindow->toggleEditMode(); + return true; + } + } + + return false; +} + +void KBabelView::processUriDrop(KURL::List& uriList, const QPoint& pos) +{ + // if we have two entries, the chance is high, that it + // is a drag from the catalog manager + if(uriList.count() == 2) + { + int result = _dropMenu->exec(pos); + switch(result) + { + case ID_DROP_OPEN: + { + KURL first(uriList.first()); + KURL second(uriList.last()); + + if( KIO::NetAccess::exists(first, true, this) ) + { + open(first); + } + else + { + openTemplate(second,first); + } + break; + } + case ID_DROP_OPEN_TEMPLATE: + { + open(uriList.last()); + break; + } + } + } + else + { + // okay, we have one URI.. process it + KURL url( uriList.first() ); + + // load in the file + open(url); + } +} + +void KBabelView::forwardMsgstrEditCmd(EditCommand* cmd) +{ +/* + if(cmd->terminator()!=0) + { + kdDebug(KBABEL) << QString::number(cmd->terminator()) << endl; + } + else + { + DelTextCmd* delcmd = (DelTextCmd*) cmd; + kdDebug(KBABEL) << QString::number(delcmd->offset)+":"+delcmd->str+"|" << endl; + } +*/ + bool fuzzyRemoved=false; + if(KBabelSettings::autoUnsetFuzzy() && _catalog->isFuzzy(_currentIndex) ) + { + fuzzyRemoved=true; + + _catalog->applyBeginCommand(_currentIndex,Msgstr,this); + + removeFuzzyStatus(); + } + + cmd->setPart(Msgstr); + cmd->setIndex(_currentIndex); + + bool wasUntranslated=_catalog->isUntranslated(_currentIndex); + + _catalog->applyEditCommand(cmd,this); + + if( fuzzyRemoved ) + { + _catalog->applyEndCommand(_currentIndex,Msgstr,this); + } + + + bool isUntranslated=_catalog->isUntranslated(_currentIndex); + + if(wasUntranslated && !isUntranslated) + emit signalUntranslatedDisplayed(false); + else if(!wasUntranslated && isUntranslated) + emit signalUntranslatedDisplayed(true); + +} + +void KBabelView::autoCheck() +{ + autoCheck(true); +} + + +void KBabelView::autoCheck(bool onlyWhenChanged) +{ + if( !_autocheckTools.isEmpty() ) + { + QStringList oldStatus = _catalog->itemStatus(_currentIndex); + + QStringList status = _catalog->itemStatus( _currentIndex,true, _autocheckTools ); + + // if there is more than one view, the status changes only in + // one view, so we have to update always. + if(_catalog->isLastView() && onlyWhenChanged && oldStatus == status) + return; + + if( !status.isEmpty() ) + { + QString msg = ""; + + // ### TODO: whynot use i18n("context",text) directly? + KLocale* locale=KGlobal::locale(); + + for( QStringList::iterator it=status.begin() ; it != status.end() ; ++it ) + { + if( msg.isEmpty() ) msg = locale->translate("what check found errors",(*it).utf8()); + else msg += ", "+locale->translate("what check found errors",(*it).utf8()); + } + + //i18n: translators: Status bar text that automatic checks have found some errors + emit signalChangeStatusbar(i18n("1 error: %1", "%n errors: %1", status.size ()).arg(msg)); + emit signalFaultyDisplayed(true); + + if(KBabelSettings::autoCheckColorError()) + { + msgstrEdit->setCurrentColor( MsgMultiLineEdit::ErrorColor ); + } + + if(KBabelSettings::beepOnError() && !_dontBeep) + { + if(onlyWhenChanged) + { + if(oldStatus != status && oldStatus.isEmpty() ) + { + KNotifyClient::beep(); + } + } + else if(isActiveWindow()) + { + KNotifyClient::beep(); + } + } + } + else if( _catalog->itemStatus(_currentIndex).isEmpty() ) + { + _catalog->removeFromErrorList(_currentIndex); + + emit signalFaultyDisplayed(false); + + if(KBabelSettings::autoCheckColorError()) + { + msgstrEdit->setCurrentColor( MsgMultiLineEdit::NormalColor ); + } + } + } +} + +void KBabelView::spellcheckAll() +{ + spell.what2Check=All; + spellcheck(); +} + +void KBabelView::spellcheckAllMulti() +{ + spell.what2Check=AllMulti; + spellcheck(); +} + + +void KBabelView::spellcheckFromCursor() +{ + spell.what2Check=End; + spellcheck(); +} + + +void KBabelView::spellcheckCurrent() +{ + spell.what2Check=Current; + spellcheck(); +} + +void KBabelView::spellcheckFromCurrent() +{ + spell.what2Check=BeginCurrent; + spellcheck(); +} + + +void KBabelView::spellcheckMarked() +{ + if(!msgstrEdit->hasSelectedText()) + { + return; + } + + spell.what2Check=Marked; + spellcheck(); +} + + +void KBabelView::spellcheckCommon() +{ + SpellDlg *spellDlg = new SpellDlg(msgstrEdit->hasSelectedText(),this + ,"SpellDlg"); + + if(spellDlg->exec()) + { + if(spellDlg->all()) + spell.what2Check=All; + else if(spellDlg->current()) + spell.what2Check=Current; + else if(spellDlg->begin()) + spell.what2Check=Begin; + else if(spellDlg->end()) + spell.what2Check=End; + else if(spellDlg->marked()) + spell.what2Check=Marked; + else if(spellDlg->beginCurrent()) + spell.what2Check=BeginCurrent; + else + { + kdError() << "unhandled option in spell dialog" << endl; + return; + } + + spellcheck(); + } + + delete spellDlg; +} + +void KBabelView::addSpellcheckWords( uint pos, QString text, uint index, uint form ) +{ + // special format chars + QString spclChars="abfnrtv'\"?\\"; + QChar accelMarker=_catalog->miscSettings().accelMarker; + + uint textLength=text.length(); + do + { + QString word=""; + bool wordBegin=false; + while(!wordBegin && pos < textLength) + { + QChar c=text[pos]; + if(c.isLetter() || c==accelMarker) + { + wordBegin=true; + } + else if( c == '\\') + { + if(pos+1 < textLength && spclChars.contains(text[pos+1]) ) + { + pos+=2; + } + else + { + // consider it to be unnecessary escaped character + pos++; + } + } + else + { + pos++; + } + } + int begin=pos; + + bool wordEnd=false; + while(!wordEnd && pos < textLength) + { + if(text[pos].isLetter() || text[pos]=='-' || (text[pos]=='\'' && !spell.config->dictionary().startsWith("malti"))) + { + word+=text[pos]; + pos++; + } + else if(text[pos]==accelMarker) + { + pos++; + } + else if(text[pos]=='\n') + { + // newline without \n counts as nothing + pos++; + } + else + { + wordEnd=true; + } + } + + int end=pos; + + // remove '-' and accelMarker at the end of a word + while(text[end]=='-' || (text[pos]=='\'' && !spell.config->dictionary().startsWith("malti")) + || text[end]==accelMarker) + { + end--; + word.truncate(word.length()-1); + } + + if(!word.isEmpty()) + { + spell.wordList.append(word); + Position *pos = new Position; + pos->index=index; + pos->form=form; + pos->pos=begin; + pos->end=end; + spell.posDict.append(pos); + } + } + while(pos < textLength); +} + +void KBabelView::spellcheck() +{ + if(isReadOnly() || spell.active) + return; + + spell.wordList.clear(); + spell.posDict.clear(); + spell.ignoreList.clear(); + spell.newIgnoreList.clear(); + + spell.misspelled=0; + spell.replaced=0; + spell.lastIndex=0; + spell.posCorrection=0; + spell.lastPos=0; + spell.inWordCorrection=0; + + if( !_spellcheckSettings.valid ) + { + _spellcheckSettings = _project->spellcheckSettings(); + } + + spell.config = new KSpellConfig(this,"tempSpellConfig"); + spell.config->setNoRootAffix(_spellcheckSettings.noRootAffix); + spell.config->setRunTogether(_spellcheckSettings.runTogether); + spell.config->setClient(_spellcheckSettings.spellClient); + spell.config->setEncoding(_spellcheckSettings.spellEncoding); + spell.config->setDictionary(_spellcheckSettings.spellDict); + + if(spell.what2Check==Marked) + { + spell.lastIndex=_currentIndex; + + _tagExtractor->setString(msgstrEdit->selectedText()); + QString marked=_tagExtractor->plainString(true); + + addSpellcheckWords(msgstrEdit->beginOfMarkedText(),marked + ,_currentIndex,msgstrEdit->currentForm()); + } + else + { + uint first=0; + uint last=_catalog->numberOfEntries()-1; + QString text; + + bool emitProgress=false; + + if(spell.what2Check==All || spell.what2Check==Begin + || spell.what2Check==End || spell.what2Check==AllMulti + || spell.what2Check==BeginCurrent) + { + emitProgress=true; + } + + if(spell.what2Check==Begin) + { + first=0; + last=_currentIndex-1; + } + else if(spell.what2Check==End) + { + first=_currentIndex+1; + last=_catalog->numberOfEntries()-1; + + int pos=msgstrEdit->currentIndex(); + int form=msgstrEdit->currentForm(); + + QStringList msgs = _catalog->msgstr(_currentIndex); + _tagExtractor->setString((*msgs.at(form))); + text=_tagExtractor->plainString(true); + addSpellcheckWords( pos, text, _currentIndex, form++ ); + + for( QStringList::Iterator i=msgs.at(form++) ; i!=msgs.end(); i++) + { + _tagExtractor->setString(*i); + text=_tagExtractor->plainString(true); + addSpellcheckWords( pos, text, _currentIndex, form++ ); + } + } + else if(spell.what2Check==BeginCurrent) + { + first=_currentIndex; + last=_catalog->numberOfEntries()-1; + } + else if(spell.what2Check!=All && spell.what2Check!=AllMulti) + { + first=last=_currentIndex; + } + + if(emitProgress) + { + emit signalResetProgressBar(i18n("Preparing spell check"),100); + kapp->processEvents(100); + } + + uint total=last-first+1; + uint lastPercent=0; + for(uint i=first; i <= last; i++) + { + if(emitProgress && 100*i/ QMAX(total,1) > lastPercent) + { + lastPercent++; + emit signalProgress(lastPercent); + + kapp->processEvents(100); + } + + QStringList msgs=_catalog->msgstr(i); + uint formCounter=0; + for(QStringList::Iterator j=msgs.begin() ; j!=msgs.end() ; ++j) + { + _tagExtractor->setString(*j); + text=_tagExtractor->plainString(true); + addSpellcheckWords(0,text,i,formCounter++); + } + } + + if(spell.what2Check==Begin) + { + int pos=msgstrEdit->currentIndex(); + int form=msgstrEdit->currentForm(); + + QStringList msgs = _catalog->msgstr(_currentIndex); + _tagExtractor->setString((*msgs.at(form)).left(pos)); + text=_tagExtractor->plainString(true); + addSpellcheckWords( 0, text, _currentIndex, form++ ); + + for( QStringList::Iterator i=msgs.at(form++) ; i!=msgs.end(); i++) + { + _tagExtractor->setString(*i); + text=_tagExtractor->plainString(true); + addSpellcheckWords( 0, text, _currentIndex, form++ ); + } + } + + if(emitProgress) + { + emit signalClearProgressBar(); + } + } + + if(!spell.wordList.isEmpty()) + { + spell.active=true; + _dontBeep=true; + + spell.kspell= new KSpell (this, i18n("Spellcheck"), + this, SLOT(spellStart(KSpell *)), spell.config, true, true); + if( spell.kspell->status() == KSpell::Error ) + { + KMessageBox::error( this, i18n("KBabel cannot start spell checker. " + "Please verify your KDE installation.") ); + return; + } + + connect(spell.kspell, SIGNAL(death()),this, SLOT(spellCleanDone())); + + connect(spell.kspell, SIGNAL(misspelling(const QString &, const QStringList & + , unsigned int)), this + , SLOT(spellMisspelled(const QString &, const QStringList &, unsigned int))); + + connect(spell.kspell, SIGNAL(corrected(const QString &, const QString &, unsigned int)) + , this, SLOT(spellCorrected(const QString &, const QString &, unsigned int))); + + connect(spell.kspell,SIGNAL(ignoreall(const QString &)) + , this, SLOT(spellAddIgnore(const QString &))); + + connect(spell.kspell, SIGNAL(done(bool)) + , this, SLOT(spellResult(bool))); + + spell.kspell->setAutoDelete(true); // let KSpell handle delete + } + else + { + KMessageBox::information(this,i18n( + "No relevant text has been found for spell checking.")); + } +} + +void KBabelView::spellStart(KSpell *) +{ + // set ignored words + if(_spellcheckSettings.rememberIgnored) + { + QString urlString = _spellcheckSettings.ignoreURL; + if(urlString.contains("@PACKAGE@")) + { + urlString.replace("@PACKAGE@",_catalog->packageName()); + } + // ### TODO: correctly set the URL; support for MostLocalURL + KURL url(urlString); + if(url.isLocalFile()) + { + QFile file(url.path()); + if(file.open(IO_ReadOnly)) + { + QTextStream stream(&file); + stream.setEncoding(QTextStream::UnicodeUTF8); + QString contents = stream.read(); + file.close(); + + spell.ignoreList = QStringList::split('\n',contents); + } + else if(file.exists()) + { + KMessageBox::sorry(this, + i18n("Error opening the file that contains words " + "to ignore during spell checking:\n" + "%1").arg(file.name())); + } + } + else + { + KMessageBox::sorry(this, + i18n("Only local files are allowed for saving " + "ignored words to during spell checking:\n" + "%1").arg(urlString)); + } + + if(spell.ignoreList.count() > 0) + { + emit signalResetProgressBar(i18n("Preparing spell check"),100); + kapp->processEvents(100); + + uint total = spell.ignoreList.count(); + uint oldPercent=0; + uint counter=0; + QStringList::Iterator it; + for(it=spell.ignoreList.begin(); it != spell.ignoreList.end(); ++it) + { + counter++; + if(counter/total > oldPercent) + { + oldPercent++; + emit signalProgress(oldPercent); + kapp->processEvents(100); + } + + spell.kspell->ignore(*it); + } + + emit signalClearProgressBar(); + } + } + + spell.kspell->checkList(&spell.wordList); +} + + +bool KBabelView::markMisspelled(const QString &orig, unsigned int pos) +{ + Position *p = spell.posDict.at(pos); + if(!p) + { + kdError() << "not a valid position: " << pos << endl; + return false; + } + + if(p->index != _currentIndex || (p->index==_currentIndex && p->form!=msgstrEdit->currentForm())) + { + DocPosition pos; + pos.item=p->index; + pos.form=p->form; + gotoEntry(pos); + } + + + if(p->index != spell.lastIndex) + { + spell.lastIndex=p->index; + spell.posCorrection=0; + } + if(pos != spell.lastPos) + { + spell.lastPos=pos; + spell.inWordCorrection=0; + } + + int x=0; + int y=0; + + int begin=p->pos+spell.posCorrection-spell.inWordCorrection; + int end=p->end+spell.posCorrection-spell.inWordCorrection; + + // check if this is the correct word + QString text = *_catalog->msgstr(p->index).at(p->form); + text=text.mid(begin,end-begin); + QChar accelMarker=_catalog->miscSettings().accelMarker; + + if(text.contains(accelMarker)) + { + text.replace(accelMarker,""); + } + if(text.contains('\n')) + { + text.replace("\n",""); + } + + bool textOk=true; + if(text != orig) + { + // if text and orig are not the same, + // maybe it was a word with hyphens + int n=text.contains('-'); + n+=text.contains('\''); + if( n > 0 ) + { + // re-get the original text since we replace some things above + QString text = *_catalog->msgstr(p->index).at(p->form); + text=text.mid(begin,end-begin); + + bool textFound=false; + int i = 0; + int e=-1; + while(!textFound && i <= n) + { + int lastPos=e+1; + e = text.find('-',lastPos); + int tmp = text.find('\'',lastPos); + if(e < 0 && tmp > 0) + { + e=tmp; + } + else if(e > 0 && tmp > 0 && tmp < e) + { + e=tmp; + } + + if(e<0) e=text.length(); + + QString w=text.mid(lastPos,e-lastPos); + if(w.contains(accelMarker)) + { + w.replace(accelMarker,""); + } + if(text.contains('\n')) + { + text.replace("\n",""); + } + if( w == orig) + { + textFound=true; + end=begin+e; + begin=begin+lastPos; + } + + i++; + } + + if(!textFound) + { + textOk=false; + } + } + else + { + textOk=false; + } + } + + int beginx, beginy; + + msgstrEdit->offset2Pos(end,y,x); + msgstrEdit->offset2Pos(begin,beginy,beginx); + msgstrEdit->setSelection(beginy,beginx,y,x); + + if(!textOk) + { + text = *_catalog->msgstr(p->index).at(p->form); + text=text.mid(begin,end-begin); + kdDebug(KBABEL) << "Sync error: given: " << orig << " have: " << text << endl; + cancelSpellcheck(); + + KMessageBox::error(this,i18n( + "There seems to be an error with the synchronization " + "of the spell checking process and KBabel.\n" + "Please check that you have set the correct settings for " + "your language for spell checking.\n" + "If you have, and this problem is reproducible, please " + "send a detailed bug report (your spell checking options, " + "what file you have checked and what to do to reproduce " + "the problem) by using Help->Report Bug...")); + } + + return textOk; +} + +void KBabelView::spellMisspelled(const QString &orig, const QStringList &, unsigned int pos) +{ + kdDebug(KBABEL) << "misspelled: " << orig << " pos: " << pos << endl; + + spell.misspelled++; + + markMisspelled(orig,pos); +} + +void KBabelView::spellCorrected(const QString &orig, const QString &word, unsigned int pos) +{ + if(orig != word) + { + QString newWord(word); + kdDebug(KBABEL) << "corrected: " << orig << " " << newWord + << " pos: " << pos << endl; + + if(spell.replaced==0) + { + // handle the spell check as one action + int index; + Position *p = spell.posDict.at(pos); + if(p) + { + index=p->index; + } + else + { + index=_currentIndex; + } + + _catalog->applyBeginCommand(index,Msgstr,this); + } + + spell.replaced++; + + + if(markMisspelled(orig,pos)) + { + QString marked=msgstrEdit->selectedText(); + spell.origWords.append(marked); + + if(marked.contains("\n") && !newWord.contains('\n')) + { + QString s1=newWord; + s1.replace(" ","\n"); + + // if only a newline has been replaced with a white space + if(s1==marked) + { + newWord.replace(" "," \n"); + } + + } + // check if the old word had an accelerator. If yes and the new + // word has no accelerator, try to add the accelerator for + // the same char else add in at the same position + QChar accelMarker=_catalog->miscSettings().accelMarker; + if(marked.contains(accelMarker) && !newWord.contains(accelMarker)) + { + int b=marked.find(accelMarker); + QChar accel=marked[b+1]; + int nb=newWord.find(accel,0,false); + if(nb>=0) + { + newWord.insert(nb,accelMarker); + } + // if the new word does not contain the old accel char + // set the accelerator to the same position as the old + else + { + if((uint)b >= newWord.length()) + b = 0; + newWord.insert(b,accelMarker); + } + } + + spell.newWords.append(newWord); + + msgstrEdit->cut(); + int row=0; + int col=0; + msgstrEdit->getCursorPosition(&row,&col); + msgstrEdit->insertAt(newWord,row,col,true); + + int newCorrection = newWord.length() - marked.length(); + spell.posCorrection += newCorrection; + spell.inWordCorrection += newCorrection; + + // now store the new position + Position *p = spell.posDict.at(pos); + if(p) + { + p->end=p->end+newCorrection; + } + } + } +} + + +void KBabelView::spellResult(bool flag) +{ + kdDebug(KBABEL) << "spellResult: " << flag << endl; + + + if(spell.replaced > 0) + { + // close the spell check action + _catalog->applyEndCommand(spell.lastIndex,Msgstr,this); + } + + + if(flag) + { + emit signalChangeStatusbar(i18n("Spellcheck: %n word replaced","Spellcheck: %n words replaced",spell.replaced)); + + if(spell.misspelled==0) + { + KMessageBox::information(this,i18n( + "Spellcheck successfully finished.\n" + "No misspelled words have been found.")); + } + else if(spell.replaced > 0 && spell.what2Check!=Current + && spell.what2Check!=Marked) + { + QStringList list; + QStringList::Iterator origIt; + QStringList::Iterator newIt; + origIt=spell.origWords.begin(); + newIt=spell.newWords.begin(); + + for(;origIt != spell.origWords.end() + && newIt != spell.newWords.end(); origIt++,newIt++) + { + list.append(*origIt+" -> "+*newIt); + } + + } + + if( spell.what2Check!=AllMulti && spell.misspelled!=0 ) + KMessageBox::information(this,i18n("Spellcheck: %n word replaced","Spellcheck: %n words replaced",spell.replaced)); + + if(_spellcheckSettings.rememberIgnored) + { + if(spell.newIgnoreList.count() > 0) + { + KURL url(_spellcheckSettings.ignoreURL); + if(url.isLocalFile()) + { + QFile file(url.path()); + if(file.open(IO_WriteOnly|IO_Append)) + { + QStringList::Iterator it; + QTextStream stream(&file); + stream.setEncoding(QTextStream::UnicodeUTF8); + + for(it=spell.newIgnoreList.begin(); + it!=spell.newIgnoreList.end(); + ++it) + { + stream << *it << "\n"; + } + + file.close(); + } + } + else + { + kdDebug(KBABEL) << "only local files are supported for storing" + << "ignored words" << endl; + } + } + } + } + else + { + emit signalChangeStatusbar(i18n("Spellcheck canceled")); + if(spell.replaced > 0) + undo(); + } + + int s=spell.kspell->dlgResult(); + spell.kspell->cleanUp(); + + emit signalSpellcheckDone(s); + QTimer::singleShot(0,this,SLOT(cleanUpSpellStruct())); +} + + +void KBabelView::spellCleanDone() +{ + kdDebug(KBABEL) << "spellCleanDone" << endl; + + // if the pointer is cleared, you have finished correcly + if( !spell.kspell ) return; + + KSpell::spellStatus status = spell.kspell->status(); + + if(status == KSpell::Error || status == KSpell::Crashed) + { + cleanUpSpellStruct(); + } + + if(status == KSpell::Error) + { + KMessageBox::sorry(this, i18n("The spell checker program could not be started.\n" + "Please make sure you have the spell checker program properly " + "configured and in your PATH.")); + } + else if(status == KSpell::Crashed) + { + KMessageBox::sorry(this, i18n("The spell checker program seems to have crashed.")); + } +} + +void KBabelView::cleanUpSpellStruct() +{ + kdDebug(KBABEL) << "Cleaning structure" << endl; + // spell.kspell is set to be autodeleted + spell.kspell = 0; + delete spell.config; + spell.config=0; + spell.wordList.clear(); + spell.posDict.clear(); + spell.origWords.clear(); + spell.newWords.clear(); + spell.ignoreList.clear(); + spell.newIgnoreList.clear(); + spell.active = false; + _dontBeep=false; +} + +void KBabelView::cancelSpellcheck() +{ + spell.active=false; +} + +void KBabelView::spellAddIgnore(const QString &word) +{ + if(!spell.ignoreList.contains(word)) + { + spell.newIgnoreList.append(word); + } +} + +void KBabelView::forwardSearchStart() +{ + emit signalResetProgressBar(i18n("Searching"),100); + emit signalSearchActive(true); +} + +void KBabelView::forwardSearchStop() +{ + emit signalClearProgressBar(); + emit signalSearchActive(false); +} + +void KBabelView::forwardProgressStart(const QString& msg) +{ + emit signalResetProgressBar(msg,100); +} + +void KBabelView::slotAutoSaveTimeout( ) +{ + if ( isModified( ) ) + { + autoSaveTimer->stop( ); + saveFile( false ); + autoSaveTimer->start( 1000 * 60 * _autoSaveDelay ); + } +} + +void KBabelView::useProject (Project::Ptr project) +{ + // FIXME: close the current project first + disconnect (_project, SIGNAL(signalSpellcheckSettingsChanged()), + this, SLOT(updateProjectSettings())); + + _project = project; + _catalog->useProject(_project); + + readProject(_project); + + connect (project, SIGNAL(signalSpellcheckSettingsChanged()), + this, SLOT(updateProjectSettings())); +} + +#include "kbabelview.moc" |