/* ****************************************************************************
  This file is part of KBabel

  Copyright (C) 1999-2000 by Matthias Kiefer
                            <matthias.kiefer@gmx.de>

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

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

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

**************************************************************************** */

#include "kbabelview.h"
#include "prefwidgets.h"

#include <qlayout.h>

#include <qlabel.h>
#include <qframe.h>
#include <qsplitter.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 <kcharsets.h>
#include <kcmenumngr.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 <kspell.h>
#include <kwin.h>
#include <kstdaccel.h>

#include "resources.h"

#include "catalogitem.h"
#include "editcmd.h"
#include "finddialog.h"
#include "gotodialog.h"
#include "headereditor.h"
#include "kbabeldictbox.h"
#include "kbcatalog.h"
#include "msgfmt.h"
#include "mymultilineedit.h"
#include "settings.h"
#include "spelldlg.h"
#include "tagextractor.h"

#include "version.h"


#define ID_DROP_OPEN          1
#define ID_DROP_OPEN_TEMPLATE 2

#define MAX_HISTORY 50

QPtrList<KBabelView> *KBabelView::viewList = 0;

KBabelView::KBabelView(KBCatalog* catalog,QWidget *parent, QString configFile)
    : QWidget(parent), _configFile(configFile)
{
    if(!viewList)
        viewList = new QPtrList<KBabelView>;

    viewList->append(this);
    
    if (catalog == 0)
      kdFatal(KBABEL) << "catalog==0" << endl;

    _catalog=catalog;
    _catalog->registerView(this);

    KConfig* config;
    if( _configFile.isEmpty() ) config=KGlobal::config();
    else config= new KConfig(_configFile);
    
    KConfigGroupSaver gs(config,"Editor");
    bool buildLeds=!config->readBoolEntry("LedInStatusbar",Defaults::Editor::ledInStatusbar);


    _fuzzyLed=0;
    _untransLed=0;
    _errorLed=0;
    _currentIndex=0;
    _gotoDialog=0;
    _findDialog=0;
    _replaceDialog=0;
    _replaceAskDialog=0;
    _tagsMenu=0;
    _argsMenu=0;
    _autoSearchTempDisabled=false;
    _diffEnabled=false;
    _loadingDiffFile=false;
    _diffing=false;
    _dontBeep=false;
    
    _showTryLaterBox=true;

    _editingDocumentation=false;

    spell.posDict.setAutoDelete(true);
    spell.active=false;

    setAcceptDrops(true);


    QHBoxLayout* topLayout=new QHBoxLayout(this);

    _viewSplitter = new QSplitter(this,"_viewSplitter");
    _viewSplitter->setOpaqueResize(true);
    topLayout->addWidget(_viewSplitter);

    _mainEditSplitter=new QSplitter(_viewSplitter,"_mainEditSplitter");
    _mainEditSplitter->setOpaqueResize(true);
    _mainEditSplitter->setOrientation(Qt::Vertical);
    _mainEditSplitter->setFrameStyle(QFrame::Box | QFrame::Sunken);

    QWidget *tempWidget=new QWidget(_mainEditSplitter,"msgidWidget");
    tempWidget->setMinimumSize(300,150);


    QVBoxLayout *layout=new QVBoxLayout(tempWidget);

    msgidLabel = new MsgMultiLineEdit(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\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(2003));

    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>"));



    tempWidget=new QWidget(_mainEditSplitter,"msgstrWidget");
    tempWidget->setMinimumSize(300,150);

    layout=new QVBoxLayout(tempWidget);

    msgstrEdit = new MsgMultiLineEdit(tempWidget,"msgstrEdit");
    msgstrEdit->installEventFilter(this);
    KCursor::setAutoHideCursor(msgstrEdit,true);
    connect( msgstrEdit, SIGNAL( textChanged() ), msgstrEdit, SLOT( emittedTextChanged() ));

    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(buildLeds)
    {
       _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);

       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>"));



    _toolBoxSplitter=new QSplitter(_viewSplitter,"_toolBoxSplitter");
    _toolBoxSplitter->setOpaqueResize(true);
    _toolBoxSplitter->setOrientation(Qt::Vertical);
    _toolBoxSplitter->setFrameStyle(QFrame::Box | QFrame::Sunken);

    _commentWidget=new QWidget(_toolBoxSplitter,"commentWidget");
    layout=new QVBoxLayout(_commentWidget);

    commentEdit = new MsgMultiLineEdit(_commentWidget);
    commentEdit->setMinimumHeight(50);
    commentEdit->setSpacePoints(false);
    commentEdit->setHighlightSyntax(false);
    commentEdit->installEventFilter(this);
    KCursor::setAutoHideCursor(commentEdit,true);

    label=new QLabel(commentEdit,i18n("&Comment:"),_commentWidget);

    hb=new QHBoxLayout(layout);
    hb->addSpacing(KDialog::marginHint());
    hb->addWidget(label);

    layout->addWidget(commentEdit);
    layout->setStretchFactor(commentEdit,1);

    QWhatsThis::add(_commentWidget,
       i18n("<qt><p><b>Comment Editor</b></p>\n\
This edit window shows you the comments of the currently displayed message.<p>\n\
<p>The comments normally contain information about where the message is found in the source\n\
code and status information about this message (fuzzy, c-format).\n\
Hints from other translators are also sometimes contained in comments.</p>\n\
<p>You can hide the comment editor by deactivating\n\
<b>Options->Show Comments</b>.</p></qt>"));

    _toolsWidget = new QTabWidget(_toolBoxSplitter,"toolTab");
    _toolsWidget->setTabPosition(QTabWidget::Bottom);
    connect(_toolsWidget, SIGNAL(currentChanged(QWidget*))
                        , this, SLOT(updateTool(QWidget*)));

    dictBox = new KBabelDictBox(_toolsWidget,"dictBox");
    _toolsWidget->addTab(dictBox,i18n("the search (noun)","Se&arch"));

    contextView = new QTextView(_toolsWidget, "contextView");
    KCursor::setAutoHideCursor(contextView->viewport(),true);
    _toolsWidget->addTab(contextView,i18n("PO C&ontext"));

    sourceView = new SourceContext(_toolsWidget);
    _toolsWidget->addTab(sourceView,i18n("Source"));
    
    taglistView = new KListBox(_toolsWidget);
    _toolsWidget->addTab(taglistView,i18n("Tags"));

    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>"));

    QWhatsThis::add(contextView,
        i18n("<qt><p><b>PO Context</b></p>"
             "<p>This window shows the context of the current message "
	     "in the PO file. Normally it shows four messages in front "
	     "of the current message and four after it.</p>"
	     "<p>You can hide the tools window by deactivating "
	     "<b>Options->Show Tools</b>.</p></qt></qt>"));

    QValueList<int> sizeList;
    sizeList.append(1);
    sizeList.append(3);

    _toolBoxSplitter->setSizes(sizeList);

    sizeList.clear();
    sizeList.append(3);
    sizeList.append(2);
    _viewSplitter->setSizes(sizeList);

    msgstrEdit->setReadOnly(true);
    commentEdit->setReadOnly(true);

    connect(msgstrEdit,SIGNAL(signalUndoCmd(EditCommand*)),this,SLOT(forwardMsgstrEditCmd(EditCommand*)));
    connect(commentEdit,SIGNAL(signalUndoCmd(EditCommand*)),this,SLOT(forwardCommentEditCmd(EditCommand*)));


    connect(msgstrEdit,SIGNAL(textChanged()),this
            ,SIGNAL(signalMsgstrChanged()));
    connect(this,SIGNAL(signalMsgstrChanged()),this,SLOT(autoCheck()));

    connect(msgidLabel,SIGNAL(cursorPositionChanged(int,int))
            , this, SIGNAL(signalCursorPosChanged(int,int)));
    connect(msgstrEdit,SIGNAL(cursorPositionChanged(int,int))
            , this, SIGNAL(signalCursorPosChanged(int,int)));
    connect(commentEdit,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(QString)),this
            ,SLOT(showError(QString)));

    connect(taglistView,SIGNAL(doubleClicked(QListBoxItem*))
            , this, SLOT(insertTagFromTool(QListBoxItem*)));

    connect(_catalog,SIGNAL(signalSettingsChanged(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("Open"),ID_DROP_OPEN);
    _dropMenu->insertItem(i18n("Open Template"),ID_DROP_OPEN_TEMPLATE);

    readSettings(config);

    setMinimumSize(400,300);


    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;
   }
}

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 project)
{
    if(!viewList)
        return 0;
    
    QPtrListIterator<KBabelView> it(*viewList);
    KBabelView *view=0;
    while( it.current() && !view)
    {
        KURL cu = it.current()->currentURL();
	if( cu.isEmpty() && it.current()->project()==project)
	    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 msgstrEdit->isOverwriteMode();
}


void KBabelView::setOverwriteMode(bool ovr)
{
   msgstrEdit->setOverwriteMode(ovr);
   commentEdit->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()==EditCommand::Msgstr)
      {
         msgstrEdit->processCommand(cmd,undo);
	 emit signalMsgstrChanged();
      }
      else if(cmd->part()==EditCommand::Comment)
      {
         commentEdit->processCommand(cmd,undo);
      }
   }
}



void KBabelView::readSettings(KConfig* config)
{
   KConfigGroupSaver saver(config,"Editor");

   _settings.autoUnsetFuzzy=config->readBoolEntry("AutoUnsetFuzzy"
		   ,Defaults::Editor::autoUnsetFuzzy);
   if(_settings.autoUnsetFuzzy)
   {
      connect(msgstrEdit,SIGNAL(textChanged())
		  ,this,SLOT(autoRemoveFuzzyStatus()));
   }

   _settings.cleverEditing=config->readBoolEntry("CleverEditing"
			   ,Defaults::Editor::cleverEditing);
   _settings.autoCheckArgs=config->readBoolEntry("AutoCheckArgs"
			   ,Defaults::Editor::autoCheckArgs);
   _settings.autoCheckAccel=config->readBoolEntry("AutoCheckAccel"
			   ,Defaults::Editor::autoCheckAccel);
   _settings.autoCheckEquation=config->readBoolEntry("AutoCheckEquation"
			   ,Defaults::Editor::autoCheckEquation);
   _settings.autoCheckContext=config->readBoolEntry("AutoCheckContext"
			   ,Defaults::Editor::autoCheckContext);
   _settings.autoCheckSingularPlural=config->readBoolEntry("AutoCheckSingularPlural"
			   ,Defaults::Editor::autoCheckSingularPlural);
   _settings.autoCheckXmlTags=config->readBoolEntry("AutoCheckXmlTags"
			   ,Defaults::Editor::autoCheckXmlTags);
   _settings.beepOnError=config->readBoolEntry("BeepOnError"
			   ,Defaults::Editor::beepOnError);
   _settings.autoCheckColorError=config->readBoolEntry("AutoCheckColorError"
			   ,Defaults::Editor::autoCheckColorError);
   
   _diffEnabled = config->readBoolEntry("AutoDiff", false);
   emit signalDiffEnabled(_diffEnabled);
   _settings.useDBForDiff = config->readBoolEntry("UseDBForDiff"
           ,Defaults::Editor::useDBForDiff);
   _settings.diffAddUnderline = config->readBoolEntry("DiffAddUnderline"
           ,Defaults::Editor::diffAddUnderline);
   _settings.diffDelStrikeOut = config->readBoolEntry("DiffDelStrikeOut"
           ,Defaults::Editor::diffDelStrikeOut);
   _settings.diffAddColor=config->readColorEntry("DiffAddColor"
			   ,&Defaults::Editor::diffAddColor);
   _settings.diffDelColor=config->readColorEntry("DiffDelColor"
			   ,&Defaults::Editor::diffDelColor);
   _settings.diffBaseDir=config->readEntry("DiffBaseDir"
			   ,Defaults::Editor::diffBaseDir);

   _settings.cleverEditing=config->readBoolEntry("CleverEditing"
			   ,Defaults::Editor::cleverEditing);
   _settings.highlightBg=config->readBoolEntry("HighlightBackground"
			   ,Defaults::Editor::highlightBg);
   _settings.highlightSyntax=config->readBoolEntry("HighlightSyntax"
			   ,Defaults::Editor::highlightSyntax);
   _settings.quotes=config->readBoolEntry("EnableQuotes"
			   ,Defaults::Editor::quotes);
   _settings.whitespacePoints=config->readBoolEntry("WhitespacePoints"
			   ,Defaults::Editor::whitespacePoints);

   _settings.bgColor=config->readColorEntry("BackgroundColor"
			   ,&Defaults::Editor::bgColor);
   _settings.quotedColor=config->readColorEntry("QuotedColor"
			   ,&Defaults::Editor::quotedColor);
   _settings.errorColor=config->readColorEntry("ErrorColor"
			   ,&Defaults::Editor::errorColor);
   _settings.cformatColor=config->readColorEntry("CformatColor"
			   ,&Defaults::Editor::cformatColor);
   _settings.accelColor=config->readColorEntry("AccelColor"
			   ,&Defaults::Editor::accelColor);
   _settings.tagColor=config->readColorEntry("TagColor"
			   ,&Defaults::Editor::tagColor);

   QFont defaultFont=Defaults::Editor::msgFont();
   _settings.msgFont=config->readFontEntry("MsgFont",&defaultFont);

   _settings.ledInStatusbar=config->readBoolEntry("LedInStatusbar"
			   ,Defaults::Editor::ledInStatusbar);
   _settings.ledColor=config->readColorEntry("LedColor"
			   ,&Defaults::Editor::ledColor);


   KSpellConfig spellConf(0,"spellconfig");

   _settings.noRootAffix=config->readBoolEntry("NoRootAffix"
			   ,spellConf.noRootAffix());
   _settings.runTogether=config->readBoolEntry("RunTogether"
			   ,spellConf.runTogether());
   _settings.spellEncoding=config->readNumEntry("SpellEncoding"
			   ,spellConf.encoding());
   _settings.spellClient=config->readNumEntry("SpellClient"
			   ,spellConf.client());
   _settings.spellDict=config->readEntry("SpellDictionary"
			   ,spellConf.dictionary());
   _settings.rememberIgnored=config->readBoolEntry("RememberIgnored"
			   ,Defaults::Editor::rememberIgnored);
   _settings.ignoreURL=config->readEntry("IgnoreURL"
			   ,Defaults::Editor::ignoreURL());

   if(_fuzzyLed)
   {
      _fuzzyLed->setColor(_settings.ledColor);
   }
   if(_untransLed)
   {
      _untransLed->setColor(_settings.ledColor);
   }
   if(_errorLed)
   {
      _errorLed->setColor(_settings.ledColor);
   }

   msgstrEdit->setCleverEditing(_settings.cleverEditing);

   msgstrEdit->setHighlightBg(_settings.highlightBg);
   msgidLabel->setHighlightBg(_settings.highlightBg);

   msgstrEdit->setHighlightSyntax(_settings.highlightSyntax);
   msgidLabel->setHighlightSyntax(_settings.highlightSyntax);

   msgstrEdit->setQuotes(_settings.quotes);
   msgidLabel->setQuotes(_settings.quotes);

   msgstrEdit->setSpacePoints(_settings.whitespacePoints);
   msgidLabel->setSpacePoints(_settings.whitespacePoints);

   msgstrEdit->setFont(_settings.msgFont);
   msgidLabel->setFont(_settings.msgFont);

   msgstrEdit->setBgColor(_settings.bgColor);
   msgidLabel->setBgColor(_settings.bgColor);

   msgstrEdit->setHighlightColors(_settings.quotedColor,_settings.errorColor
           ,_settings.cformatColor,_settings.accelColor,_settings.tagColor);
   msgidLabel->setHighlightColors(_settings.quotedColor,_settings.errorColor
           ,_settings.cformatColor,_settings.accelColor,_settings.tagColor);
   
   msgidLabel->setDiffDisplayMode(_settings.diffAddUnderline
           ,_settings.diffDelStrikeOut);
   msgidLabel->setDiffColors(_settings.diffAddColor, _settings.diffDelColor);

   config->setGroup("Search");
   _searchSettings.autoSearch=config->readBoolEntry("AutoSearch"
           ,Defaults::Search::autoSearch);
   _searchSettings.defaultModule=config->readEntry("DefaultModule",
                   Defaults::Search::defaultModule);

   dictBox->readSettings(config);
   sourceView->restoreSettings(config);
}

void KBabelView::saveSettings(KConfig* config)
{
   KConfigGroupSaver saver(config,"Editor");

   config->writeEntry("AutoUnsetFuzzy",_settings.autoUnsetFuzzy);
   config->writeEntry("AutoCheckArgs",_settings.autoCheckArgs);
   config->writeEntry("AutoCheckAccel",_settings.autoCheckAccel);
   config->writeEntry("AutoCheckEquation",_settings.autoCheckEquation);
   config->writeEntry("AutoCheckContext",_settings.autoCheckContext);
   config->writeEntry("AutoCheckSingularPlural",_settings.autoCheckSingularPlural);
   config->writeEntry("AutoCheckColorError",_settings.autoCheckColorError);
   config->writeEntry("BeepOnError",_settings.beepOnError);

   config->writeEntry("AutoDiff",_diffEnabled);
   config->writeEntry("UseDBForDiff",_settings.useDBForDiff);
   config->writeEntry("DiffAddUnderline",_settings.diffAddUnderline);
   config->writeEntry("DiffDelStrikeOut",_settings.diffDelStrikeOut);
   config->writeEntry("DiffAddColor",_settings.diffAddColor);
   config->writeEntry("DiffDelColor",_settings.diffDelColor);
   config->writeEntry("DiffBaseDir",_settings.diffBaseDir);

   config->writeEntry("CleverEditing",_settings.cleverEditing);
   config->writeEntry("HighlightBackground",_settings.highlightBg);
   config->writeEntry("HighlightSyntax",_settings.highlightSyntax);
   config->writeEntry("EnableQuotes",_settings.quotes);
   config->writeEntry("WhitespacePoints",_settings.whitespacePoints);

   config->writeEntry("BackgroundColor",_settings.bgColor);
   config->writeEntry("QuotedColor",_settings.quotedColor);
   config->writeEntry("ErrorColor",_settings.errorColor);
   config->writeEntry("CformatColor",_settings.cformatColor);
   config->writeEntry("AccelColor",_settings.accelColor);
   config->writeEntry("TagColor",_settings.tagColor);

   config->writeEntry("MsgFont",_settings.msgFont);

   config->writeEntry("LedInStatusbar",_settings.ledInStatusbar);
   config->writeEntry("LedColor",_settings.ledColor);

   config->writeEntry("NoRootAffix",_settings.noRootAffix);
   config->writeEntry("RunTogether",_settings.runTogether);
   config->writeEntry("SpellEncoding",_settings.spellEncoding);
   config->writeEntry("SpellClient",_settings.spellClient);
   config->writeEntry("SpellDictionary",_settings.spellDict);
   config->writeEntry("RememberIgnored",_settings.rememberIgnored);
   config->writeEntry("IgnoreURL",_settings.ignoreURL);

   config->setGroup("Search");
   config->writeEntry("AutoSearch",_searchSettings.autoSearch);
   config->writeEntry("DefaultModule",_searchSettings.defaultModule);

   dictBox->saveSettings(config);
   sourceView->saveSettings(config);

   _catalog->savePreferences(config);

   config->sync();
}


void KBabelView::setSettings(CatManSettings settings)
{
    _catManSettings = settings;
}

void KBabelView::setSettings(EditorSettings settings)
{
   if(_settings.autoUnsetFuzzy!=settings.autoUnsetFuzzy)
   {
      if(!settings.autoUnsetFuzzy)
      {
         disconnect(msgstrEdit,SIGNAL(textChanged())
			 ,this,SLOT(autoRemoveFuzzyStatus()));
      }
      else
      {
         connect(msgstrEdit,SIGNAL(textChanged())
			 ,this,SLOT(autoRemoveFuzzyStatus()));
      }
   }

   msgstrEdit->setCleverEditing(settings.cleverEditing);

   if(settings.highlightBg != msgstrEdit->highlightBg())
   {
      msgstrEdit->setHighlightBg(settings.highlightBg);
      msgidLabel->setHighlightBg(settings.highlightBg);
   }

   if(settings.highlightSyntax != msgstrEdit->highlightSyntax())
   {
      msgstrEdit->setHighlightSyntax(settings.highlightSyntax);
      msgidLabel->setHighlightSyntax(settings.highlightSyntax);
   }

   if(settings.quotes != msgstrEdit->quotes())
   {
      msgstrEdit->setQuotes(settings.quotes);
      msgidLabel->setQuotes(settings.quotes);
   }

   if(settings.whitespacePoints != msgstrEdit->spacePoints())
   {
      msgstrEdit->setSpacePoints(settings.whitespacePoints);
      msgidLabel->setSpacePoints(settings.whitespacePoints);
   }

   msgstrEdit->setFont(settings.msgFont);
   msgidLabel->setFont(settings.msgFont);

   msgstrEdit->setBgColor(settings.bgColor);
   msgidLabel->setBgColor(settings.bgColor);

   msgstrEdit->setHighlightColors(settings.quotedColor,settings.errorColor
           ,settings.cformatColor,settings.accelColor,settings.tagColor);
   msgidLabel->setHighlightColors(settings.quotedColor,settings.errorColor
           ,settings.cformatColor,settings.accelColor,settings.tagColor);

   msgidLabel->setDiffDisplayMode(settings.diffAddUnderline
           ,settings.diffDelStrikeOut);
   msgidLabel->setDiffColors(settings.diffAddColor, settings.diffDelColor);
   
   // if errors should not use special color, reset the color of the text
   if(!settings.autoCheckColorError)
   {
	msgstrEdit->setCurrentColor( MsgMultiLineEdit::NormalColor);
   }

   if(settings.ledColor != _settings.ledColor)
   {
      if(_fuzzyLed)
      {
         _fuzzyLed->setColor(settings.ledColor);
      }
      if(_untransLed)
      {
         _untransLed->setColor(settings.ledColor);
      }
      if(_errorLed)
      {
         _errorLed->setColor(settings.ledColor);
      }


      emit ledColorChanged(settings.ledColor);
   }

   _settings=settings;

   autoCheck(false);
}

void KBabelView::setSettings(SearchSettings settings)
{
   if(_searchSettings.defaultModule!=settings.defaultModule)
      _autoSearchTempDisabled=false;

   _searchSettings=settings;
}

SearchSettings KBabelView::searchSettings() const
{
	return _searchSettings;
}

void KBabelView::setSettings(SourceContextSettings settings)
{
    sourceView->setSettings(settings);
}

SourceContextSettings KBabelView::sourceContextSettings() const
{
    return sourceView->settings();
}

void KBabelView::setRMBEditMenu(QPopupMenu* popup)
{
	msgidLabel->setContextMenu( popup );
	msgstrEdit->setContextMenu( popup );
	commentEdit->setContextMenu( popup );
	KContextMenuManager::insert(contextView,popup);
	KContextMenuManager::insert(contextView->viewport(),popup);
	KContextMenuManager::insert(this,popup);
}

void KBabelView::setRMBSearchMenu(QPopupMenu* popup)
{
   dictBox->setRMBMenu(popup);
}


void KBabelView::saveView(KConfig *config)
{
   KConfigGroupSaver saver(config,"View");

   config->writeEntry("MainSplitter", _viewSplitter->sizes());
   config->writeEntry("ToolboxSplitter",_toolBoxSplitter->sizes());
   config->writeEntry("EditSplitter",_mainEditSplitter->sizes());

   config->writeEntry("Toolbox", _toolsWidget->currentPageIndex());
}

void KBabelView::restoreView(KConfig *config)
{
   KConfigGroupSaver saver(config,"View");

   QValueList<int> sizes=config->readIntListEntry("MainSplitter");
   _viewSplitter->setSizes(sizes);

   sizes = config->readIntListEntry("ToolboxSplitter");
   _toolBoxSplitter->setSizes(sizes);

   sizes =config->readIntListEntry("EditSplitter");
   if(sizes.isEmpty())
   {
       sizes.append(1);
       sizes.append(1);
   }
   _mainEditSplitter->setSizes(sizes);
}

void KBabelView::saveSession(KConfig* config)
{
   QString focus;
   int line=0,col=0;
   if(msgstrEdit->hasFocus())
   {
      focus="msgstr";
      msgstrEdit->getCursorPosition(&line,&col);
   }
   else if(commentEdit->hasFocus())
   {
      focus="comment";
      commentEdit->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("URL",currentURL().url());

   if(isModified())
   {
      QString tempFile=kapp->tempSaveName(currentURL().url());
      kdDebug(KBABEL) << (QString("tempSaveName: %s")+tempFile) << endl;
      _catalog->saveFileAs(tempFile,true);

      config->writeEntry("tempSaveName",tempFile);
   }

   config->writeEntry("DefaultSearchModule"
                   ,_searchSettings.defaultModule);
   config->writeEntry("AutoSearch",_searchSettings.autoSearch);
   config->writeEntry("AutoUnsetFuzzy",_settings.autoUnsetFuzzy);
   config->writeEntry("AutoCheckArgs",_settings.autoCheckArgs);
   config->writeEntry("AutoCheckAccel",_settings.autoCheckAccel);
   config->writeEntry("AutoCheckEquation",_settings.autoCheckEquation);
   config->writeEntry("AutoCheckContext",_settings.autoCheckContext);
   config->writeEntry("AutoCheckSingularPlural"
           ,_settings.autoCheckSingularPlural);
   config->writeEntry("AutoCheckColorError",_settings.autoCheckColorError);
   config->writeEntry("BeepOnError",_settings.beepOnError);


   config->writeEntry("AutoDiff",_diffEnabled);
   config->writeEntry("UseDBForDiff",_settings.useDBForDiff);
   config->writeEntry("DiffAddUnderline",_settings.diffAddUnderline);
   config->writeEntry("DiffDelStrikeOut",_settings.diffDelStrikeOut);
   config->writeEntry("DiffAddColor",_settings.diffAddColor);
   config->writeEntry("DiffDelColor",_settings.diffDelColor);
   config->writeEntry("DiffBaseDir",_settings.diffBaseDir);


   config->writeEntry("NoRootAffix",_settings.noRootAffix);
   config->writeEntry("RunTogether",_settings.runTogether);
   config->writeEntry("SpellEncoding",_settings.spellEncoding);
   config->writeEntry("SpellClient",_settings.spellClient);
   config->writeEntry("SpellDictionary",_settings.spellDict);
   config->writeEntry("RememberIgnored",_settings.rememberIgnored);
   config->writeEntry("IgnoreURL",_settings.ignoreURL);


}

void KBabelView::restoreSession(KConfig* config)
{
   QString url=config->readEntry("URL");
   // if config has this key, the modified file was not saved
   // but saved in a temp file
   if(config->hasKey("tempSaveName"))
   {
      open(config->readEntry("tempSaveName"));
      _catalog->setCurrentURL(url);
      _catalog->setModified(true);
   }
   else if(!url.isEmpty())
   {
      open(url);
   }


   bool oldValue=_settings.autoUnsetFuzzy;
   _settings.autoUnsetFuzzy=config->readBoolEntry("AutoUnsetFuzzy",Defaults::Editor::autoUnsetFuzzy);

   if(oldValue!=_settings.autoUnsetFuzzy)
   {
      if(!_settings.autoUnsetFuzzy)
      {
 disconnect(msgstrEdit,SIGNAL(textChanged()),this,SLOT(autoRemoveFuzzyStatus()));
      }
      else
      {
 connect(msgstrEdit,SIGNAL(textChanged()),this,SLOT(autoRemoveFuzzyStatus()));
      }
   }

   _settings.autoCheckArgs=config->readBoolEntry("AutoCheckArgs"
   ,Defaults::Editor::autoCheckArgs);
   _settings.autoCheckAccel=config->readBoolEntry("AutoCheckAccel"
   ,Defaults::Editor::autoCheckAccel);
   _settings.autoCheckEquation=config->readBoolEntry("AutoCheckEquation"
   ,Defaults::Editor::autoCheckEquation);
   _settings.autoCheckContext=config->readBoolEntry("AutoCheckContext"
            ,Defaults::Editor::autoCheckContext);
   _settings.autoCheckSingularPlural=config->readBoolEntry("AutoCheckSingularPlural"
            ,Defaults::Editor::autoCheckSingularPlural);
    _settings.beepOnError=config->readBoolEntry("BeepOnError"
            ,Defaults::Editor::beepOnError);
   _settings.autoCheckColorError=config->readBoolEntry("AutoCheckColorError"
            ,Defaults::Editor::autoCheckColorError);
   
   _diffEnabled = config->readBoolEntry("AutoDiff", false);
   emit signalDiffEnabled(_diffEnabled);
   _settings.useDBForDiff = config->readBoolEntry("UseDBForDiff"
           ,Defaults::Editor::useDBForDiff);
   _settings.diffAddUnderline = config->readBoolEntry("DiffAddUnderline"
           ,Defaults::Editor::diffAddUnderline);
   _settings.diffDelStrikeOut = config->readBoolEntry("DiffDelStrikeOut"
           ,Defaults::Editor::diffDelStrikeOut);
   _settings.diffAddColor=config->readColorEntry("DiffAddColor"
			   ,&Defaults::Editor::diffAddColor);
   _settings.diffDelColor=config->readColorEntry("DiffDelColor"
			   ,&Defaults::Editor::diffDelColor);
   _settings.diffBaseDir=config->readEntry("DiffBaseDir"
			   ,Defaults::Editor::diffBaseDir);

   msgidLabel->setDiffDisplayMode(_settings.diffAddUnderline
           ,_settings.diffDelStrikeOut);
   msgidLabel->setDiffColors(_settings.diffAddColor, _settings.diffDelColor);


  _searchSettings.autoSearch=config->readBoolEntry("AutoSearch"
                  ,Defaults::Search::autoSearch);
   QString oldModule=_searchSettings.defaultModule;
   _searchSettings.defaultModule=config->readEntry("DefaultSearchModule",
	      Defaults::Search::defaultModule);

   if(_searchSettings.defaultModule!=oldModule)
      _autoSearchTempDisabled=false;


   KSpellConfig spellConf(0,"spellconfig");
   _settings.noRootAffix=config->readBoolEntry("NoRootAffix"
			   ,spellConf.noRootAffix());
   _settings.runTogether=config->readBoolEntry("RunTogether"
		   ,spellConf.runTogether());
   _settings.spellEncoding=config->readNumEntry("SpellEncoding"
		   ,spellConf.encoding());
   _settings.spellClient=config->readNumEntry("SpellClient"
		   ,spellConf.client());
   _settings.spellDict=config->readEntry("SpellDictionary"
		   ,spellConf.dictionary());
   _settings.rememberIgnored=config->readBoolEntry("RememberIgnored"
		   ,Defaults::Editor::rememberIgnored);
   _settings.ignoreURL=config->readEntry("IgnoreURL"
		   ,Defaults::Editor::ignoreURL());

   // go to last displayed entry
   gotoEntry(config->readNumEntry("Index"));

   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=="comment")
   {
      commentEdit->setFocus();
      commentEdit->setCursorPosition(line,col);
   }
   else if(focus=="msgid")
   {
      commentEdit->setFocus();
      commentEdit->setCursorPosition(line,col);
   }
   else if(focus=="searchbox")
   {
      dictBox->setFocus();
   }


}

void KBabelView::newFileOpened(bool readOnly)
{
    if(_gotoDialog) 
    {
        _gotoDialog->setMax(_catalog->numberOfEntries());
    }

    msgstrEdit->setReadOnly(readOnly);
    commentEdit->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);

    _autoSearchTempDisabled=true;
    gotoEntry(0);
    _autoSearchTempDisabled=false;
 
   if(isActiveWindow() && _searchSettings.autoSearch)
   {
      startSearch(true);
   }
}

void KBabelView::open()
{
   open(KURL());
}

void KBabelView::open(const KURL& _url, const QString & package, bool checkIfModified)
{
   KURL url = _url;
   url.cleanPath();

   KURL cu = currentURL();
   cu.cleanPath();
   if(checkIfModified && !url.isEmpty() && cu==url)
   {
       KWin::setActiveWindow(topLevelWidget()->winId());
       return;
   }
   
   stopSearch();

   if(!checkIfModified || checkModified())
   {
       if(url.isEmpty())
       {
            QString filename;
            if ((url = KFileDialog::getOpenURL(currentURL().url(), i18n("*.po *.pot|Gettext files")
                            ,this)).isEmpty())
            {
                return;
            }
       }
       // deactive editor
       /*setEnabled(false);
       setCursor(KCursor::waitCursor());*/

       KBabelView *v = viewForURL(url,_configFile);
       
       if(v && v != this)
       {
           KWin::setActiveWindow(v->topLevelWidget()->winId());
           return;
       }
       
       bool errorInHeader=false;

       KBCatalog::IOStatus stat=_catalog->openURL(url,errorInHeader, package);

       switch(stat)
       {
          case KBCatalog::OK:
          {
              if(errorInHeader)
              {
                  KMessageBox::information(this,
                  i18n("There was an error while reading the file header. "
                       "Please check the header." ));
                  editHeader();
              }
              break;
          }
          case KBCatalog::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.url()));
              break;
          }
          case KBCatalog::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");
             if(errorInHeader)
             {
                 msg+=i18n(
                      "\nThere was also an error while reading the header.\n"
                      "Please check the header.");
             }

             KMessageBox::information(this,msg);
             emitEntryState();

             if(errorInHeader)
                 editHeader();

             break;
          }
          case KBCatalog::NO_PERMISSIONS:
          {
             KMessageBox::error(this,i18n(
               "You don't have permissions to read file:\n %1").arg(url.url()));
             break;
          }
          case KBCatalog::NO_FILE:
          {
             KMessageBox::error(this,i18n(
                 "You have not specified a valid file:\n %1").arg(url.url()));
             break;
          }
          default:
          {
             KMessageBox::error(this,i18n(
                  "Error while trying to open file:\n %1").arg(url.url()));
             break;
          }

       }
       /*
       //activate editor
       setEnabled(true);
       setCursor(KCursor::arrowCursor());*/

       if(_catalog->hasPluralForms())
       {
           KMessageBox::information(this
                 ,i18n("This file contains gettext plural forms.\n"
                     "These were introduced by gettext 0.10.36 and "
                     "are not yet really supported by KBabel.\n"
                     "You can only edit the singular form but "
                     "the other forms will not be lost. You just "
                     "have to edit them manually afterwards."
                     ));
       }

   }
}

void KBabelView::revertToSaved()
{
    if(isModified())
    {
        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());*/

       bool errorInHeader=false;

       KBCatalog::IOStatus stat=_catalog->openURL(openURL,saveURL,errorInHeader);

       switch(stat)
       {
           case KBCatalog::OK:
           {
               if(errorInHeader)
               {
                    KMessageBox::information(this,
                       i18n("There was an error while reading the file header. "
                      "Please check the header." ));
                    editHeader();
               }
               break;
           }
           case KBCatalog::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.url()));
               break;
           }
           case KBCatalog::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");
		if(errorInHeader)
		{
			msg+=i18n("\nThere was also an error while reading the header.\n"
				  "Please check the header.");
		}

		KMessageBox::information(this,msg);
		emitEntryState();

		if(errorInHeader)
			editHeader();

		break;
	  }
	  case KBCatalog::NO_PERMISSIONS:
	  {
	      KMessageBox::error(this,i18n("You don't have permissions to read file:\n %1").arg(openURL.url()));
	      break;
	  }
	  case KBCatalog::NO_FILE:
	  {
	      KMessageBox::error(this,i18n("You have not specified a valid file:\n %1").arg(openURL.url()));
	     break;
	  }
	  default:
	  {
	     KMessageBox::error(this,i18n("Error while trying to open file:\n %1").arg(openURL.url()));
	     break;
	  }

       }

       /*
       //activate editor
       setEnabled(true);

       setCursor(KCursor::arrowCursor());*/

       if(_catalog->hasPluralForms())
       {
           KMessageBox::information(this
                 ,i18n("This file contains gettext plural forms.\n"
                     "These were introduced by gettext 0.10.36 and "
                     "are not yet really supported by KBabel.\n"
                     "You can only edit the singular form but "
                     "the other forms will not be lost. You just "
                     "have to edit them manually afterwards."
                     ));
       }
   }
}

bool KBabelView::saveFile(bool syntaxCheck)
{
    if(_catalog->isReadOnly())
   {
      return saveFileAs();
   }
   else
   {
      KBCatalog::IOStatus stat=_catalog->saveFile();

      int whatToDo;

      switch(stat)
      {
	 case KBCatalog::OK:
	 {
	    if(msgstrEdit->isModified()) // after save put current edit into dictionary as well
		informDictionary();
	    if(syntaxCheck && _catalog->saveSettings().autoSyntaxCheck)
	       return checkSyntax(true,false);
	    else
	       return true;
	 }
	 case KBCatalog::NO_PERMISSIONS:
	 {
	    whatToDo=KMessageBox::warningContinueCancel(this,
	     i18n("You don't have permission to write to file:\n%1\n"
		  "Do you want to save to another file or cancel?").arg(_catalog->currentURL().url()),
	     i18n("Error"),i18n("&Save"));
	     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().url()),
	     i18n("Error"),i18n("&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(),i18n("*.po *.pot|Gettext files"),this)).isEmpty())
       {
	  return false;
       }
       newName=true;
   }

   if (KIO::NetAccess::exists(url))
   {
       if(KMessageBox::warningContinueCancel(this,QString("<qt>%1</qt>").arg(i18n("The file %1 already exists. "
						       "Do you want to overwrite it?").arg(url.url())),i18n("Warning"),i18n("&Overwrite"))==KMessageBox::Cancel)
      {
	 return false;
      }

   }

   bool wasReadOnly=_catalog->isReadOnly();

   KBCatalog::IOStatus stat=_catalog->saveFileAs(url,true);


   // if the file was not saved sucessfully ask for saving to another file
   if(stat!=KBCatalog::OK)
   {
      bool cancelLoop=false; // flag, if the saving loop should be canceled
      do
      {
	  // select the right error message
	  QString message;
	  switch(stat)
	  {
	      case KBCatalog::NO_PERMISSIONS:
	      {
		  message=i18n("You don't have permission to write to file:\n%1\n"
			       "Do you want to save to another file or cancel?").arg(url.url());
		 break;
	      }
	      case KBCatalog::NO_FILE:
	      {
		 message=i18n("You have specified a directory:\n%1\n"
			      "Do you want to save to another file or cancel?").arg(url.url());
		 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.url());
		  break;
	      }
	  }

	  // now ask, if the user wants to save to another file or cancel
	  switch(KMessageBox::warningContinueCancel(this,message,i18n("Error"),i18n("&Save")))
	  {
	     // save to another file
	     case KMessageBox::Continue:
	     {
		// ask for new filename
		if ((url = KFileDialog::getSaveURL(currentURL().url(),i18n("*.po *.pot|Gettext files"),this)).isEmpty())
		{
		   // if no filename was given cancel all
		   return false;
		}

		if (KIO::NetAccess::exists(url))
		{
		    if(KMessageBox::warningContinueCancel(this,i18n("The file %1 already exists.\n"
								    "Do you want to overwrite it?").arg(url.url()),i18n("Warning"),i18n("&Overwrite"))==KMessageBox::Continue)
		   {
		      stat=_catalog->saveFileAs(url);
		      if(stat!=KBCatalog::OK)
			 cancelLoop=false;
		      else
			cancelLoop=true;
		   }
		}
		else
		{
		   stat=_catalog->saveFileAs(url);
		   if(stat!=KBCatalog::OK)
		      cancelLoop=false;
		   else
		     cancelLoop=true;
		}
		break;
	     }
	     default: // the user don't want to save to another file
		return false;
	  }
      }
      while(!cancelLoop);
   }


   if(wasReadOnly)
   {
      msgstrEdit->setReadOnly(false);
      commentEdit->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
   if(msgstrEdit->isModified()) 
	informDictionary();
	
   if(syntaxCheck && _catalog->saveSettings().autoSyntaxCheck)
     return checkSyntax(true,false);
   else
     return true;
}

bool KBabelView::saveFileSpecial()
{
    KDialogBase *_prefDialog = new KDialogBase( this, 0, true,
	i18n("Special Save Settings"), KDialogBase::Cancel|KDialogBase::Ok|KDialogBase::Help);
    _prefDialog->setHelp("preferences_save");
    
    SavePreferences* _prefWidget = new SavePreferences(_prefDialog);
    _prefDialog->setMainWidget(_prefWidget);
    
    _prefWidget->setSettings( _catalog->saveSettings() );
    
    if( _prefDialog->exec() == QDialog::Accepted )
    {
	SaveSettings settings;
	_prefWidget->mergeSettings(settings);
	SaveSettings originalSettings = _catalog->saveSettings();
	_catalog->setSettings(settings);
	bool result = saveFileAs();
	
	_catalog->setSettings(originalSettings);
	 
	return result;
    }
    
    return false;
}

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 );

   switch(result)
   {
      case Msgfmt::Ok:
      {
          bool plFormsOk = _catalog->checkSingularPlural(false);

          if(plFormsOk)
          {
              if(!msgOnlyAtError)
              {
                  KMessageBox::information(this
                          ,i18n("The file is syntactically correct.\n\n\
Output of \"msgfmt --statistics\":\n")+output);
              }
              returnCode=true;
          }
          else
          {
              QString msg=i18n("An error in the plural forms has been found!\n"
                      "(If there is no real error, please check the "
                      "respective setting in the Identity tab of "
                      "the preferences dialog.)");
              if(question)
              {
                  switch( KMessageBox::warningContinueCancel(this,
                       msg+i18n("\nDo you want to continue or cancel and edit the file again?")
                       ,i18n("Warning"),i18n("Continue")) )
                  {
                        case KMessageBox::Continue:
                            returnCode=true;
                            break;
                        default:
                            returnCode=false;
                  }
              }
              else
              {
                  KMessageBox::error(this
                          ,msg+i18n("\nPlease edit the file again!"));
                  returnCode=false;
              }
          }
          break;
      }
      case Msgfmt::SyntaxError:
      {
          QString msg=i18n("msgfmt detected a syntax error!\n\n\
Output of \"msgfmt --statistics\":\n")+output;

          if(question)
          {
              switch( KMessageBox::warningContinueCancel(this,
                    msg+i18n("\nDo you want to continue or cancel and edit the file again?")
                    ,i18n("Warning"),i18n("Continue")) )
              {
                    case KMessageBox::Continue:
                        returnCode=true;
                        break;
                    default:
                        returnCode=false;
              }
          }
          else
          {
              KMessageBox::error(this
                      ,msg+i18n("\nPlease edit the file again!"));
              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)
            {
                switch( KMessageBox::warningContinueCancel(this,
		msg+i18n("\nDo you want to continue or cancel and edit the file again?")
		,i18n("Warning"),i18n("Continue") ))
                {
                    case KMessageBox::Continue:
                        returnCode=true;
                        break;
                    default:
                        returnCode=false;
                }
            }
            else
            {
                KMessageBox::error(this
                        ,msg+i18n("\nPlease edit the file again!"));
                returnCode=false;
            }

            break;
      }
   }

   emitEntryState();

   return returnCode;
}


bool KBabelView::checkArgs()
{
	if(currentURL().isEmpty())
		return false;

	bool result=_catalog->checkArgs(true);
	emitEntryState();

	if(result)
	{
		KMessageBox::information(this
					,i18n("No mismatch has been found.")
					, i18n("Title in Dialog: Check Arguments","Check Arguments"));
	}
	else
	{
		int index=0;
		if(!_catalog->hasError(0))
			index = _catalog->nextError(0);
		if(index>=0)
	    {
		      gotoEntry(index);
		}

		KMessageBox::error(this
					,i18n("Some mismatches have been found.\n"
			"Please check the questionable entries by using "
			"Go->Next error")
					, i18n("Title in Dialog: Check Arguments","Check Arguments"));

	}


	return result;
}




bool KBabelView::checkAccels()
{
	if(currentURL().isEmpty())
		return false;

	bool result = _catalog->checkAccelerators(true);

	emitEntryState();

	if(result)
	{
		KMessageBox::information(this
					,i18n("No mismatch has been found.")
					,i18n("Title n dialog: Check Accelerators"
							,"Check Accelerators"));
	}
	else
	{
		int index=0;
		if(!_catalog->hasError(0))
			index = _catalog->nextError(0);
		if(index>=0)
	    {
		      gotoEntry(index);
		}

		KMessageBox::error(this
					,i18n("Some mismatches have been found.\n"
			"Please check the questionable entries by using "
			"Go->Next error")
					,i18n("title in dialog: Check Accelerators"
							,"Check Accelerators"));

	}


	return result;

}




bool KBabelView::checkEquations()
{
	if(currentURL().isEmpty())
		return false;

	bool result = _catalog->checkEquations(true);

	emitEntryState();

	if(result)
	{
		KMessageBox::information(this
					,i18n("No mismatch has been found.")
					,i18n("title in dialog: Check Equations","Check Equations"));
	}
	else
	{
		int index=0;
		if(!_catalog->hasError(0))
			index = _catalog->nextError(0);
		if(index>=0)
	    {
		      gotoEntry(index);
		}

		KMessageBox::error(this
					,i18n("Some mismatches have been found.\n"
			"Please check the questionable entries by using "
			"Go->Next error")
					,i18n("title in dialog: Check Equations","Check Equations"));
		
	}
	

	return result;
}

bool KBabelView::checkForContext()
{
	if(currentURL().isEmpty())
		return false;

	bool result = _catalog->checkForContext(true);

	emitEntryState();

	if(result)
	{
		KMessageBox::information(this
					,i18n("No translated context information found.")
					,i18n("title in dialog"
							,"Look for Translated Context Info"));
	}
	else
	{
		int index=0;
		if(!_catalog->hasError(0))
			index = _catalog->nextError(0);
		if(index>=0)
	    {
		      gotoEntry(index);
		}

		KMessageBox::error(this
					,i18n("Some translated context information has been found.\n"
			"Please check the questionable entries by using "
			"Go->Next error")
					,i18n("title in dialog: Check for Context Translation"
							,"Look for Translated Context Info"));
		
	}


	return result;

}

bool KBabelView::checkSingularPlural()
{
	if(currentURL().isEmpty())
		return false;

	bool result = _catalog->checkSingularPlural(true);

	emitEntryState();

	if(result)
	{
		KMessageBox::information(this
					,i18n("No faulty plural form found.")
					,i18n("title in dialog","Check Plural Form"));
	}
	else
	{
		int index=0;
		if(!_catalog->hasError(0))
			index = _catalog->nextError(0);
		if(index>=0)
	    {
		      gotoEntry(index);
		}

		KMessageBox::error(this
					,i18n("Some faulty plural forms have been found.\n"
                      "(If there is no real error, please check the "
                      "respective setting in the Identity tab of "
                      "the preferences dialog.)\n"
			"Please check the questionable entries by using "
			"Go->Next error")
					,i18n("title in dialog","Check plural form"));
		
	}


	return result;

}

bool KBabelView::checkXmlTags()
{
	if(currentURL().isEmpty())
		return false;

	if(!_editingDocumentation)
		return true;

	bool result = _catalog->checkXmlTags();

	emitEntryState();

	if(result)
	{
		KMessageBox::information(this
					,i18n("No mismatch has been found.")
					,i18n("title in dialog: Check Tags","Check Tags"));
	}
	else
	{
		int index=0;
		if(!_catalog->hasError(0))
			index = _catalog->nextError(0);
		if(index>=0)
	    {
		      gotoEntry(index);
		}

		KMessageBox::error(this
					,i18n("Some mismatches have been found.\n"
			"Please check the questionable entries by using "
			"Go->Next error")
					,i18n("title in dialog: Check Tags","Check Tags"));
		
	}
	

	return result;
}

bool KBabelView::checkAll()
{
	if(currentURL().isEmpty())
		return false;

	bool badresult=false;
	bool a=!_catalog->checkArgs(true);
	badresult = badresult || a;
	a=!_catalog->checkAccelerators(false);
	badresult = badresult || a;
	a=!_catalog->checkEquations(false);
	badresult = badresult || a;
	a=!_catalog->checkForContext(false);
	badresult = badresult || a;
	a=!_catalog->checkSingularPlural(false);
	badresult = badresult || a;
	if( _editingDocumentation) 
	{
	    a=!_catalog->checkXmlTags(false);
	    badresult = badresult || a;
	}

	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;
		if(!_catalog->hasError(0))
			index = _catalog->nextError(0);
		if(index>=0)
	    {
		      gotoEntry(index);
		}

		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"),
      i18n("&Save"),i18n("&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(bool delay)
{
   commentEdit->blockSignals(true);
   msgstrEdit->blockSignals(true);

   if(_settings.autoUnsetFuzzy && !msgstrEdit->isModified())
   {
       disconnect(msgstrEdit,SIGNAL(textChanged()),this,SLOT(autoRemoveFuzzyStatus()));
   }

   msgidLabel->setText(_catalog->msgid(_currentIndex));
   msgidLabel->repaint();

   msgstrEdit->setText(_catalog->msgstr(_currentIndex));
   msgstrEdit->setModified(false);
   msgstrEdit->repaint();

   if(_settings.autoUnsetFuzzy && _catalog->isFuzzy(_currentIndex))
   {
      connect(msgstrEdit,SIGNAL(textChanged()),this,SLOT(autoRemoveFuzzyStatus()));
   }


   commentEdit->setText(_catalog->comment(_currentIndex));
   commentEdit->setModified(false);
   commentEdit->repaint();


   commentEdit->blockSignals(false);
   msgstrEdit->blockSignals(false);

   
   if(contextView->isVisible())
   {
      updateContext();
      contextView->repaint();
   } else
   if(sourceView->isVisible())
	sourceView->setContext(_catalog->packageDir()+_catalog->packageName(), _catalog->context(_currentIndex));

    taglistView->clear();
    taglistView->insertStringList(_catalog->tagList(_currentIndex));
    taglistView->setCurrentItem(0);

   autoCheck(false);
   
   if(_diffEnabled)
   {
       autoDiff();
   }

   if(isActiveWindow() && _searchSettings.autoSearch 
                   && !_autoSearchTempDisabled)
   {
      startSearch(delay);
   }
}


void KBabelView::undo()
{
   if(!_catalog->isUndoAvailable())
      return;

   int newIndex=_catalog->undo();

   if(newIndex != (int)_currentIndex)
      gotoEntry(newIndex);
}

void KBabelView::redo()
{
   if(!_catalog->isRedoAvailable())
      return;

   int newIndex=_catalog->redo();

   if(newIndex != (int)_currentIndex)
      gotoEntry(newIndex);
}

void KBabelView::textCut()
{
   if(msgstrEdit->hasFocus())
   {
      msgstrEdit->cut();
   }
   else if(commentEdit->hasFocus())
   {
      commentEdit->cut();
   }
}

void KBabelView::textCopy()
{
   if(msgstrEdit->hasSelectedText())
   {
      msgstrEdit->copy();
   }
   else if(commentEdit->hasSelectedText())
   {
      commentEdit->copy();
   }
   else if(msgidLabel->hasSelectedText())
   {
      msgidLabel->copy();
   }
   else if(contextView->isVisible() && contextView->hasSelectedText())
   {
      contextView->copy();
   }
   else if(dictBox->isVisible() && dictBox->hasSelectedText())
   {
      dictBox->copy();
   }
}

void KBabelView::textPaste()
{
   if(commentEdit->hasFocus())
   {
      commentEdit->paste();
   }
   else
   {
      msgstrEdit->paste();
   }
}


bool KBabelView::findNext()
{
	if(!_findDialog)
		return false;
		
	DocPosition pos;
	pos.item=_currentIndex;			
	
	if( commentEdit->hasFocus() ) {				
		pos.part = Comment;
		int line;
		int col;
		commentEdit->getCursorPosition(&line,&col);
		pos.offset = commentEdit->pos2Offset(line,col);
	}
	else if(msgidLabel->hasFocus() ) {
		pos.part = Msgid;
		int line;
		int col;
		msgidLabel->getCursorPosition(&line,&col);
		pos.offset = msgidLabel->pos2Offset(line,col);
	}
	else if(msgstrEdit->hasFocus()){
		pos.part = Msgstr;
		int line;
		int col;
		msgstrEdit->getCursorPosition(&line,&col);
		pos.offset = msgstrEdit->pos2Offset(line,col);
	}
	
	_findStartPos=pos;
	_findBreakAtEnd=false;
	
	return findNext_internal(pos);
}

bool KBabelView::findPrev()
{
	if(!_findDialog)
		return false;

	DocPosition pos;
	pos.item=_currentIndex;			
		
	if( commentEdit->hasFocus()) {				
		pos.part = Comment;
		int line;
		int col;
		commentEdit->getCursorPosition(&line,&col);
		pos.offset = commentEdit->pos2Offset(line,col);
	}
	else if(msgidLabel->hasFocus() ) {
		pos.part = Msgid;
		int line;
		int col;
		msgidLabel->getCursorPosition(&line,&col);
		pos.offset = msgidLabel->pos2Offset(line,col);
	}
	else {
		pos.part = Msgstr;
		int line;
		int col;
		msgstrEdit->getCursorPosition(&line,&col);
		pos.offset = msgstrEdit->pos2Offset(line,col);
	}
	
	_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));
			}
			else {
				r = KMessageBox::Yes;
			}
		}
		else {
			r = KMessageBox::questionYesNo(this,i18n("End of document reached.\n"
				"Continue from the beginning?"));
		}
		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;

		}
		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?"));
		    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() << "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 = !foundFile.isEmpty() && !foundFile.isNull();
		    if( morefiles ) 
		    {
			emit open( KURL(foundFile) );
			pos.item = 0;
			pos.part = Msgid;
			pos.offset = 0;
			_catalog->findNext(&opts,pos,len);
		    } else {
			if( foundFile.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)
			gotoEntry(pos.item);
				
		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:
			{
				if(!commentEdit->isVisible())
				{
					showComments(true);
					emit signalCommentsShown();
				}
				commentEdit->selectAll(false);
				commentEdit->setFocus();
				commentEdit->offset2Pos(pos.offset,line,col);
				commentEdit->offset2Pos(pos.offset+len,endline,endcol);
				
				commentEdit->setSelection(line,col,endline,endcol);
				commentEdit->setCursorPosition(endline,endcol);

				_lastFoundString=commentEdit->selectedText();
				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));
			}
			else {
				r = KMessageBox::Yes;
			}

		}
		else {
			r = KMessageBox::questionYesNo(this,i18n("Beginning of document reached.\n"
				"Continue from the end?"));
		}
		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;
				pos.offset = _catalog->msgstr(pos.item).length();
			}
			else {
				pos.part = Msgid;
				pos.offset = _catalog->msgid(pos.item).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)
			gotoEntry(pos.item);
				
		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:
				if(!commentEdit->isVisible())
				{
					showComments(true);
					emit signalCommentsShown();
				}
				commentEdit->selectAll(false);
				commentEdit->setFocus();
				commentEdit->offset2Pos(pos.offset+len,line,col);
				commentEdit->offset2Pos(pos.offset,endline,endcol);
				
				commentEdit->setSelection(line,col,endline,endcol);
				commentEdit->setCursorPosition(endline,endcol);
				
				_lastFoundString=commentEdit->selectedText();
				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()
{
	ItemPart hadFocus;
	if(msgidLabel->hasFocus())
		hadFocus=Msgid;
	else if(commentEdit->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(commentEdit->hasFocus()) {
		marked=commentEdit->selectedText();
		commentEdit->selectAll(false);
	}
	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;			
	
			int line;
			int col;
	
			if(hadFocus == Comment && opts.inComment) {
				pos.part = Comment;
				commentEdit->getCursorPosition(&line,&col);
				pos.offset = commentEdit->pos2Offset(line,col);
			}
			else if( hadFocus == Msgid && opts.inMsgid) {
				pos.part = Msgid;
				msgidLabel->getCursorPosition(&line,&col);
				pos.offset = msgidLabel->pos2Offset(line,col);
			}
			else {
				pos.part = Msgstr;
				msgstrEdit->getCursorPosition(&line,&col);
				pos.offset = msgstrEdit->pos2Offset(line,col);
			}
		}
		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;

	ItemPart hadFocus;
	if(msgidLabel->hasFocus())
		hadFocus=Msgid;
	else if(commentEdit->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(commentEdit->hasFocus()) {
		marked=commentEdit->selectedText();
		commentEdit->selectAll(false);
	}
	else if(msgidLabel->hasFocus()) {
		marked=msgidLabel->selectedText();
		msgidLabel->selectAll(false);
	}

	if(marked==_lastFoundString)
		marked="";
		
	if( _replaceDialog->exec(marked) == QDialog::Accepted ) {
		ReplaceOptions opts=_replaceDialog->replaceOpts();
		if(opts.fromCursor) {
			_findBreakAtEnd=false;
			_replacePos.item=_currentIndex;			
	
			int line;
			int col;
			if(hadFocus==Comment && opts.inComment) {
				_replacePos.part = Comment;
				commentEdit->getCursorPosition(&line,&col);
				_replacePos.offset = commentEdit->pos2Offset(line,col);
			}
			else {
				_replacePos.part = Msgstr;
				msgstrEdit->getCursorPosition(&line,&col);
				_replacePos.offset = msgstrEdit->pos2Offset(line,col);
			}
		}
		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++;

    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);
        }
    }

   EditCommand* tmp = new BeginCommand();
   EditCommand::Part part;
   QString str;

   if(_replacePos.part==Msgstr) {
        part=EditCommand::Msgstr;
        str = _catalog->msgstr(_replacePos.item)
            .mid(_replacePos.offset,_replaceLen);
    }
    else if(_replacePos.part==Comment) {
        part = EditCommand::Comment;
        str = _catalog->comment(_replacePos.item)
            .mid(_replacePos.offset,_replaceLen);
    }
    else {
       kdWarning() << "msgid can not be changed in KBabelView::replaceNext()" 
                   << endl;
       return;
}

   tmp->setPart(part);
   tmp->setIndex(_replacePos.item);
   _catalog->applyEditCommand(tmp,this);


   DelTextCmd* delCmd = new DelTextCmd(_replacePos.offset,str);
   delCmd->setPart(part);
   delCmd->setIndex(_replacePos.item);
   _catalog->applyEditCommand(delCmd,0);

   InsTextCmd* insCmd = new InsTextCmd(_replacePos.offset,opts.replaceStr);
   insCmd->setPart(part);
   insCmd->setIndex(_replacePos.item);
   _catalog->applyEditCommand(insCmd,0);

   tmp = new EndCommand();
   tmp->setPart(part);
   tmp->setIndex(_replacePos.item);

   _catalog->applyEditCommand(tmp,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();
	
	ReplaceOptions opts=_replaceDialog->replaceOpts();
	bool success=true;

	EditCommand* tmp = new BeginCommand();	
	tmp->setPart(EditCommand::Msgstr);
	tmp->setIndex(_replacePos.item);
	_catalog->applyEditCommand(tmp,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);
			}	
		}
		
		EditCommand::Part part;
		QString str;

		if(_replacePos.part==Msgstr) {
			part=EditCommand::Msgstr;
			str = _catalog->msgstr(_replacePos.item).mid(_replacePos.offset,_replaceLen);
		}
		else if(_replacePos.part==Comment) {
			part = EditCommand::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);
		delCmd->setPart(part);
		delCmd->setIndex(_replacePos.item);
		_catalog->applyEditCommand(delCmd,0);
		
		InsTextCmd* insCmd = new InsTextCmd(_replacePos.offset,opts.replaceStr);
		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);
		}
	}
	
	tmp = new EndCommand();
	tmp->setPart(EditCommand::Msgstr);
	tmp->setIndex(_replacePos.item);

	_catalog->applyEditCommand(tmp,this);
}

void KBabelView::findNextReplace()
{
	bool success;
	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, 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() << "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(); // FIXME: this will probably work correctly
	}
}


void KBabelView::deselectAll()
{
   msgstrEdit->selectAll(false);
   commentEdit->selectAll(false);
   msgidLabel->selectAll(false);
}

void KBabelView::selectAll()
{
   if(msgstrEdit->hasFocus())
   {
      msgstrEdit->selectAll();
   }
   else if(commentEdit->hasFocus())
   {
      commentEdit->selectAll();
   }
   else if(msgidLabel->hasFocus())
   {
      msgidLabel->selectAll();
   }
}

void KBabelView::clear()
{
   if(msgstrEdit->hasFocus())
   {
      msgstrEdit->clear();
   }
   else if(commentEdit->hasFocus())
   {
      commentEdit->clear();
   }
}

void KBabelView::msgid2msgstr()
{
   EditCommand* tmp = new BeginCommand();
   tmp->setPart(EditCommand::Msgstr);
   tmp->setIndex(_currentIndex);

   _catalog->applyEditCommand(tmp,this);

   msgstrEdit->clear();
   QString text = _catalog->msgid(_currentIndex);


   // 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,"");
   }

   InsTextCmd* insCmd = new InsTextCmd(0,text);
   insCmd->setPart(EditCommand::Msgstr);
   insCmd->setIndex(_currentIndex);
   msgstrEdit->processCommand(insCmd,false);
   forwardMsgstrEditCmd(insCmd);

   tmp = new EndCommand();
   tmp->setPart(EditCommand::Msgstr);
   tmp->setIndex(_currentIndex);

   _catalog->applyEditCommand(tmp,this);

   msgstrEdit->setCursorPosition(0,0);
}

void KBabelView::search2msgstr()
{
   EditCommand* tmp = new BeginCommand();
   tmp->setPart(EditCommand::Msgstr);
   tmp->setIndex(_currentIndex);

   _catalog->applyEditCommand(tmp,this);

   msgstrEdit->clear();
   InsTextCmd* insCmd = new InsTextCmd(0,dictBox->translation());
   insCmd->setPart(EditCommand::Msgstr);
   insCmd->setIndex(_currentIndex);
   msgstrEdit->processCommand(insCmd,false);
   forwardMsgstrEditCmd(insCmd);

   tmp = new EndCommand();
   tmp->setPart(EditCommand::Msgstr);
   tmp->setIndex(_currentIndex);

   _catalog->applyEditCommand(tmp,this);

   msgstrEdit->setCursorPosition(0,0);
}


void KBabelView::gotoFirst()
{
   gotoEntry(0);
}

void KBabelView::gotoLast()
{
   gotoEntry(_catalog->numberOfEntries()-1);
}

void KBabelView::gotoNext()
{
    uint max = _catalog->numberOfEntries();
    // check, if we are already showing the last entry
    if(_currentIndex+1>=max)
    {
       return;
    }
    else
    {
       gotoEntry(_currentIndex+1);
    }

}

void KBabelView::gotoPrev()
{
   // check, if we are already showing the first entry
    if(_currentIndex==0)
    {
       return;
    }
    else
    {
       gotoEntry(_currentIndex-1);
    }

}

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;
      if(number<0)
          number=0;

      gotoEntry(number);
   }

}

void KBabelView::gotoNextFuzzy()
{
   int index=_catalog->nextFuzzy(_currentIndex);
   if(index>=0)
   {
      gotoEntry(index);
   }
}

void KBabelView::gotoPrevFuzzy()
{
   int index=_catalog->prevFuzzy(_currentIndex);
   if(index>=0)
   {
      gotoEntry(index);
   }
}

void KBabelView::gotoNextError()
{
   int index=_catalog->nextError(_currentIndex);
   if(index>=0)
   {
      _dontBeep=true;
      gotoEntry(index);
      _dontBeep=false;
   }
}

void KBabelView::gotoPrevError()
{
   int index=_catalog->prevError(_currentIndex);
   if(index>=0)
   {
      _dontBeep=true;
      gotoEntry(index);
      _dontBeep=false;
   }
}

void KBabelView::gotoNextFuzzyOrUntrans()
{
   int fuzzyIndex=_catalog->nextFuzzy(_currentIndex);
   int UntransIndex=_catalog->nextUntranslated(_currentIndex);

   if(fuzzyIndex<0)
      fuzzyIndex=UntransIndex;
   if(UntransIndex<0)
      UntransIndex=fuzzyIndex;

   int index=(fuzzyIndex < UntransIndex)? fuzzyIndex : UntransIndex;

   if(index>=0)
   {
      gotoEntry(index);
   }
}

void KBabelView::gotoPrevFuzzyOrUntrans()
{
   int fuzzyIndex=_catalog->prevFuzzy(_currentIndex);
   int UntransIndex=_catalog->prevUntranslated(_currentIndex);

   if(fuzzyIndex<0)
      fuzzyIndex=UntransIndex;
   if(UntransIndex<0)
      UntransIndex=fuzzyIndex;

   int index=(fuzzyIndex > UntransIndex)? fuzzyIndex : UntransIndex;
   if(index>=0)
   {
      gotoEntry(index);
   }
}

void KBabelView::gotoNextUntranslated()
{
   int index=_catalog->nextUntranslated(_currentIndex);
   if(index>=0)
   {
      gotoEntry(index);
   }
}

void KBabelView::gotoPrevUntranslated()
{
   int index=_catalog->prevUntranslated(_currentIndex);
   if(index>=0)
   {
      gotoEntry(index);
   }
}


void KBabelView::gotoEntry(int index, bool updateHistory)
{
   if(isSearching())
      stopSearch();

// processEvents can cause reentrant gotoEntry, if PgDown or PgUp are pressed to fast
//   kapp->processEvents();
   
   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());
      }
   }

   if(msgstrEdit->isModified())
   {
       informDictionary();
   }


   _currentIndex=index;
   emitEntryState();
   updateEditor();
   updateTags();
   updateArgs();
}

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());

   gotoEntry(index,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());

   gotoEntry(index,false);

   if(_forwardHistory.count()==0)
   {
      emit signalForwardHistory(false);
   }
   if(_backHistory.count()==1)
   {
      emit signalBackHistory(true);
   }
}




void KBabelView::showComments(bool show)
{
   if(show)
   {
      if(!_toolBoxSplitter->isVisibleTo(this))
           _toolBoxSplitter->show();

      _commentWidget->show();
   }
   else
   {
      if(!_toolsWidget->isVisibleTo(this))
           _toolBoxSplitter->hide();

      _commentWidget->hide();
   }
}

void KBabelView::showTools(bool show)
{
   if(show)
   {
      if(!_toolBoxSplitter->isVisibleTo(this))
          _toolBoxSplitter->show();

      _toolsWidget->show();
   }
   else
   {
      if(!_commentWidget->isVisibleTo(this))
           _toolBoxSplitter->hide();

      _toolsWidget->hide();
   }
}


void KBabelView::removeFuzzyStatus()
{
   if(_catalog->isPluralForm(_currentIndex))
   {
       if(KMessageBox::warningContinueCancel(this,
              i18n("This entry has gettext plural forms, which "
                   "can not yet be edited with KBabel and must "
                   "be edited manually later.\n"
                   "If you remove the fuzzy status of this entry, "
                   "you might not find the fuzzy plural forms later.")
                   ,QString::null,i18n("Unset Fuzzy Status")) 
               == KMessageBox::Cancel)
       {
           return;
       }
   }
   
   bool newState =  !_catalog->isFuzzy(_currentIndex);
   _catalog->setFuzzy(_currentIndex,newState);
   emit signalFuzzyDisplayed(newState);
}


void KBabelView::editHeader()
{
   HeaderEditor* editor=_catalog->headerEditor();

   int editHeight=editor->height();
   int editWidth=editor->width();
   int width=this->width();
   int height=this->height();

   int x=width/2-editWidth/2;
   int y=height/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)
{
   if(!_toolsWidget->isVisible())
   {
      _toolsWidget->show();

      if(!_toolBoxSplitter->isVisible())
           _toolBoxSplitter->show();

      emit signalToolsShown();
   }

   _toolsWidget->showPage(dictBox);

   QString msg = _catalog->msgid(_currentIndex,true);
   QRegExp reg=_catalog->miscSettings().contextInfo;
   if(msg.contains(reg))
   {
      msg.replace(reg,"");
   }

   dictBox->setActiveModule(_searchSettings.defaultModule);
   if(delay)
   {
      dictBox->startDelayedSearch(msg);
   }
   else
   {
      dictBox->startSearch(msg);
   }
}

void KBabelView::startSearch(const QString module)
{
   if(!_toolsWidget->isVisible())
   {
      _toolsWidget->show();

      if(!_toolBoxSplitter->isVisible())
           _toolBoxSplitter->show();

      emit signalToolsShown();
   }

   _toolsWidget->showPage(dictBox);

   QString msg = _catalog->msgid(_currentIndex,true);
   QRegExp reg=_catalog->miscSettings().contextInfo;
   if(msg.contains(reg))
   {
      msg.replace(reg,"");
   }

   dictBox->setActiveModule(module);
   dictBox->startSearch(msg);

}

void KBabelView::startSelectionSearch()
{
    startSelectionSearch(_searchSettings.defaultModule);
}

void KBabelView::startSelectionSearch(const QString module)
{
   if(!_toolsWidget->isVisible())
   {
      _toolsWidget->show();

      if(!_toolBoxSplitter->isVisible())
          _toolBoxSplitter->show();

      emit signalToolsShown();
   }

   _toolsWidget->showPage(dictBox);


   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
   {
       QString msg = _catalog->msgid(_currentIndex,true);
       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(_currentIndex+1);


   emit signalFirstDisplayed(_currentIndex==0);
   emit signalLastDisplayed(_currentIndex+1==_catalog->numberOfEntries());

   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));

   if( _catalog->hasError(_currentIndex) != isError )
   {
      isError = !isError;

      emit signalFaultyDisplayed(isError);

      if(isError)
      {
         QPalette palette=msgstrEdit->palette();
         palette.setColor( QColorGroup::Text, red );

         if( _catalog->itemStatus(_currentIndex) & CatalogItem::Syntax )
         {
             msgstrEdit->setCurrentColor( MsgMultiLineEdit::ErrorColor );
         }
         else if(_settings.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(QString message)
{
    KMessageBox::error(this,message);
}

void KBabelView::dragEnterEvent(QDragEnterEvent *event)
{
    // accept uri drops only
    event->accept(QUriDrag::canDecode(event));
}

void KBabelView::dropEvent(QDropEvent *event)
{
    QStrList uri;

    // see if we can decode a URI.. if not, just ignore it
    if (QUriDrag::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(QUriDrag::canDecode(e))
      {
         e->accept(true);
         return true;
      }
   }
   else if(event->type() == QEvent::Drop)
   {
      QStrList uri;

      // see if we can decode a URI.. if not, just ignore it
      if (QUriDrag::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( KStdAccel::isEqual( ke, KStdAccel::undo())  )
      {
         undo();
	 return true;
      }
      else if( KStdAccel::isEqual( ke, KStdAccel::redo())  )
      {
         redo();
	 return true;
      }
   }

   return false;
}

void KBabelView::processUriDrop(QStrList& 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.at(1));

              if( KIO::NetAccess::exists(first) )
              {
                 open(first);
              }
              else
              {
                 openTemplate(second,first.url());
              }
              break;
           }
           case ID_DROP_OPEN_TEMPLATE:
           {
              open(KURL(uriList.at(1)));
              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() << QString::number(cmd->terminator()) << endl;
   }
   else
   {
      DelTextCmd* delcmd = (DelTextCmd*) cmd;
      kdDebug() << QString::number(delcmd->offset)+":"+delcmd->str+"|" << endl;
   }
*/

// FIXME: "enable autoUnsetFuzzy for pluralforms after implementing full support"
   bool fuzzyRemoved=false;
   if(_settings.autoUnsetFuzzy && _catalog->isFuzzy(_currentIndex) 
           && !_catalog->isPluralForm(_currentIndex))
   {
      fuzzyRemoved=true;

      EditCommand* tmp=new BeginCommand();
      tmp->setPart(EditCommand::Msgstr);
      tmp->setIndex(_currentIndex);

      _catalog->applyEditCommand(tmp,this);
      removeFuzzyStatus();
   }

   cmd->setPart(EditCommand::Msgstr);
   cmd->setIndex(_currentIndex);


   bool wasUntranslated=_catalog->isUntranslated(_currentIndex);

   _catalog->applyEditCommand(cmd,this);

   if( fuzzyRemoved )
   {
      EditCommand* tmp=new EndCommand();
      tmp->setPart(EditCommand::Msgstr);
      tmp->setIndex(_currentIndex);

      _catalog->applyEditCommand(tmp,this);
   }


   bool isUntranslated=_catalog->isUntranslated(_currentIndex);

   if(wasUntranslated && !isUntranslated)
      emit signalUntranslatedDisplayed(false);
   else if(!wasUntranslated && isUntranslated)
      emit signalUntranslatedDisplayed(true);

}

void KBabelView::forwardCommentEditCmd(EditCommand* cmd)
{
   bool wasFuzzy=_catalog->isFuzzy(_currentIndex);

   cmd->setPart(EditCommand::Comment);
   cmd->setIndex(_currentIndex);

   _catalog->applyEditCommand(cmd,this);

   bool isFuzzy=_catalog->isFuzzy(_currentIndex);

   if(wasFuzzy && !isFuzzy)
      emit signalFuzzyDisplayed(false);
   else if(!wasFuzzy && isFuzzy)
      emit signalFuzzyDisplayed(true);
}


void KBabelView::updateContext()
{
   uint total = _catalog->numberOfEntries();
   if(total==0)
      return;

   QString text;
   uint startIndex;
   uint context = 4;
   if(_currentIndex < context)
   {
      startIndex = 0;
   }
   else if(_currentIndex > total-2*context-2)
   {
      startIndex = total-2*context-1;
   }
   else
   {
      startIndex = _currentIndex-context;
   }

   for(uint i=startIndex; i < startIndex+(2*context+1); i++)
   {
      if(i == _currentIndex)
      {
          text += "<p><hr/><b>"+i18n("current entry") +"</b><hr/></p>";
          continue;
      }

      QString entry;
      QString temp;
      temp = _catalog->comment(i);
      if(!temp.isEmpty())
      {
         temp = QStyleSheet::convertFromPlainText(temp);
         temp.replace(QRegExp("^<p>"),"");
         temp.replace(QRegExp("</p>$"),"");
         entry += "<i>"+temp+"</i><br/>";
      }

      temp = QStyleSheet::convertFromPlainText(_catalog->msgid(i));
      temp.replace(QRegExp("^<p>"),"");
      temp.replace(QRegExp("</p>$"),"");
      entry += temp + "<br/>---<br/>";

      temp = _catalog->msgstr(i);
      if(temp.isEmpty())
      {
         entry +="<i><b><font color=\"red\">"
               +i18n("untranslated")
               +"</font></b></i><br/>";
      }
      else
      {
         temp = QStyleSheet::convertFromPlainText(temp);
         temp.replace(QRegExp("^<p>"),"");
         temp.replace(QRegExp("</p>$"),"");

         entry += temp+"<br/>";
      }


      text += "<p>"+entry+"</p>";
   }
   contextView->setText("<qt>"+text+"</qt>");

   int height = contextView->contentsHeight();
   contextView->setContentsPos(0,height/2);

}

void KBabelView::updateTool(QWidget *w)
{
    if(w == contextView)
        updateContext();
    else if(w == sourceView)
	sourceView->setContext(_catalog->packageDir()+_catalog->packageName(), _catalog->context(_currentIndex));
}

void KBabelView::autoCheck()
{
    autoCheck(true);
}


void KBabelView::autoCheck(bool onlyWhenChanged)
{
   if(_settings.autoCheckArgs || _settings.autoCheckAccel
            || _settings.autoCheckEquation || _settings.autoCheckContext
            || _settings.autoCheckSingularPlural || _settings.autoCheckXmlTags)
   {
        int oldStatus = _catalog->itemStatus(_currentIndex);

        int what = 0;
        if( _settings.autoCheckArgs )
            what = what | CatalogItem::Args;
        if( _settings.autoCheckAccel && ! _editingDocumentation )
            what = what | CatalogItem::Accel;
        if( _settings.autoCheckEquation )
            what = what | CatalogItem::Equation;
		if( _settings.autoCheckContext )
			what = what | CatalogItem::Context;
		if( _settings.autoCheckSingularPlural )
			what = what | CatalogItem::SingularPlural;
		if( _settings.autoCheckXmlTags && _editingDocumentation )
			what = what | CatalogItem::XmlTags;

		int status = _catalog->itemStatus(_currentIndex,true,what);

		// 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 & what)
		{
			QString msg;
		
			if(_settings.autoCheckArgs && (status & CatalogItem::Args))
			{
				msg = i18n("what check found errors","arguments");
			}
			if(_settings.autoCheckAccel && (status & CatalogItem::Accel))
			{
				if(!msg.isEmpty())
					msg += ", ";

				msg+=i18n("what check found errors","accelerator");
			}
			if(_settings.autoCheckEquation && (status & CatalogItem::Equation))
			{
				if(!msg.isEmpty())
					msg += ", ";

				msg+=i18n("what check found errors","equation");
			}
			if(_settings.autoCheckContext && (status & CatalogItem::Context))
			{
				if(!msg.isEmpty())
					msg += ", ";

				msg+=i18n("what check found errors","context");
			}
			if(_settings.autoCheckSingularPlural 
                    && (status & CatalogItem::SingularPlural))
			{
				if(!msg.isEmpty())
					msg += ", ";

				msg+=i18n("what check found errors","plural forms");
			}
			if(_settings.autoCheckXmlTags && (status & CatalogItem::XmlTags))
			{
				if(!msg.isEmpty())
					msg += ", ";

				msg+=i18n("what check found errors","tags");
			}


			emit signalChangeStatusbar(i18n("Error in %1").arg(msg));
			emit signalFaultyDisplayed(true);
			
			if(_settings.autoCheckColorError)
			{
			    msgstrEdit->setCurrentColor( MsgMultiLineEdit::ErrorColor );
			}

   			if(_settings.beepOnError && !_dontBeep)
			{
				if(onlyWhenChanged)
				{
					if(oldStatus != status && oldStatus==CatalogItem::None)
					{
						KNotifyClient::beep();
					}
				}
				else if(isActiveWindow())
				{
					KNotifyClient::beep();
				}
			}
		}
		else if(! (_catalog->itemStatus(_currentIndex)))
		{
			_catalog->removeFromErrorList(_currentIndex);

			emit signalFaultyDisplayed(false);

			if(_settings.autoCheckColorError)
			{
			    msgstrEdit->setCurrentColor( MsgMultiLineEdit::NormalColor );
			}
		}
   }
}

void KBabelView::spellcheckAll()
{
	spell.what2Check=All;
	spellcheck();
}


void KBabelView::spellcheckFromCursor()
{
	spell.what2Check=End;
	spellcheck();
}


void KBabelView::spellcheckCurrent()
{
	spell.what2Check=Current;
	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
		{
			kdError() << "unhandled option in spell dialog" << endl;
			return;
		}

		spellcheck();
	}

	delete spellDlg;
}

		
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;
	

	uint first=0;
	uint last=_catalog->numberOfEntries()-1;
	
	bool emitProgress=false;
	
	if(spell.what2Check==All || spell.what2Check==Begin
					|| spell.what2Check==End)
	{
		emitProgress=true;
	}

	if(spell.what2Check==Begin)
	{
		first=0;
		last=_currentIndex;
	}
	else if(spell.what2Check==End)
	{
		first=_currentIndex;
		last=_catalog->numberOfEntries()-1;
	}
	else if(spell.what2Check!=All)
	{
		first=last=_currentIndex;
	}

	if(emitProgress)
	{
		emit signalResetProgressBar(i18n("Preparing spell check"),100);
		kapp->processEvents(100);
	}
	
	
	// special format chars
	QString spclChars="abfnrtv'\"?\\";

	uint total=last-first+1;
	uint lastPercent=0;
	for(uint i=first; i <= last; i++)
	{
		int posCorrection=0;
		if(emitProgress && 100*i/total > lastPercent)
		{
			lastPercent++;
			emit signalProgress(lastPercent);

			kapp->processEvents(100);
		}
						
		QString text = _catalog->msgstr(i);
		
		if(spell.what2Check==Marked)
		{
			QString marked = msgstrEdit->selectedText();

			spell.lastIndex=_currentIndex;
			posCorrection=msgstrEdit->beginOfMarkedText();

			text = text.left(posCorrection+marked.length());
		}
		else if(spell.what2Check==Begin && i==last)
		{
			int col=0;
			int row=0;

			msgstrEdit->getCursorPosition(&row,&col);
			int pos=msgstrEdit->pos2Offset(row,col);
			text=text.left(pos);
		}
		else if(spell.what2Check==End && i==first)
		{
			int col=0;
			int row=0;

			msgstrEdit->getCursorPosition(&row,&col);
			posCorrection=msgstrEdit->pos2Offset(row,col);
		}

		uint pos=posCorrection;
		uint textLength=text.length();
        TagExtractor te(text);
        text=te.plainString(true);
		QChar accelMarker=_catalog->miscSettings().accelMarker;
		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
				{
					pos++;
				}
			}
			int begin=pos;

			bool wordEnd=false;
			while(!wordEnd && pos < textLength)
			{
				if(text[pos].isLetter() || text[pos]=='-' || text[pos]=='\'')
				{
					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-1]=='-' || text[end-1] == '\'' 
                    || text[end-1]==accelMarker)
			//while(text[end-1]==accelMarker)
			{
				end--;
				word.truncate(word.length()-1);
			}
			
			if(!word.isEmpty())
			{
				//kdDebug(KBABEL) << word << endl;
				spell.wordList.append(word);
				Position *pos = new Position;
				pos->index=i;
				pos->pos=begin;
				pos->end=end;

				spell.posDict.append(pos);
			}
		}
		while(pos < textLength);
	}

	if(emitProgress)
	{
		emit signalClearProgressBar();
	}


	if(!spell.wordList.isEmpty())
	{
		spell.active=true;
		_dontBeep=true;
	
		spell.config = new KSpellConfig(this,"tempSpellConfig");
		spell.config->setNoRootAffix(_settings.noRootAffix);
		spell.config->setRunTogether(_settings.runTogether);
		spell.config->setClient(_settings.spellClient);
		spell.config->setEncoding(_settings.spellEncoding);
		spell.config->setDictionary(_settings.spellDict);


	
		spell.kspell= new KSpell (this, "KBabel: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(_settings.rememberIgnored)
	{	
		QString urlString = _settings.ignoreURL;
		if(urlString.contains("@PACKAGE@"))
		{
			urlString.replace(QRegExp("@PACKAGE@"),_catalog->packageName());
		}
		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)
	{
		gotoEntry(p->index);
	}


	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);
	text=text.mid(begin,end-begin);
	QChar accelMarker=_catalog->miscSettings().accelMarker;
	
	if(text.contains(accelMarker))
	{
		text.replace(QRegExp(accelMarker),"");
	}	
	if(text.contains('\n'))
	{
		text.replace(QRegExp("\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);
			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();
				
				kdDebug(KBABEL) << lastPos << " " << e << " " << text.mid(lastPos,e-lastPos) << endl;

				QString w=text.mid(lastPos,e-lastPos);
				if(w.contains(accelMarker))
				{
					w.replace(QRegExp(accelMarker),"");
				}
				if(text.contains('\n'))
				{
					text.replace(QRegExp("\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);
		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 spellchecking options, "
				"what file you have checked and what to do to reproduce "
				"the problem) by using Help->Report Bug..."));
	}

        // Move dialog out of the way of the msgstr
	if(textOk)
		spell.kspell->moveDlg(msgidLabel->width(), msgidLabel->y());

	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
			EditCommand *cmd = new BeginCommand();
			cmd->setPart(EditCommand::Msgstr);
			Position *p = spell.posDict.at(pos);
			if(p)
			{
				cmd->setIndex(p->index);
			}
			else
			{
				cmd->setIndex(_currentIndex);
			}
			_catalog->applyEditCommand(cmd,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;
				QRegExp ws(" ");
				s1.replace(ws,"\n");
				
				// if only a newline has been replaced with a white space
				if(s1==marked)
				{
					newWord.replace(ws," \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
		EditCommand *cmd = new EndCommand();
		cmd->setPart(EditCommand::Msgstr);
		cmd->setIndex(spell.lastIndex);

		_catalog->applyEditCommand(cmd,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(_settings.rememberIgnored)
		{
			if(spell.newIgnoreList.count() > 0)
			{
				KURL url(_settings.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();
	}
	
	spell.kspell->cleanUp();
	
	emit signalSpellcheckDone(spell.kspell->dlgResult());
	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("ISpell could not be started.\n"
       			"Please make sure you have ISpell properly "
				"configured and in your PATH."));
	}
	else if(status == KSpell::Crashed)
	{
		KMessageBox::sorry(this, i18n("ISpell 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);
}



#include "kbabelview.moc"
