/**********************************************************************
** Copyright (C) 2000-2008 Trolltech ASA.  All rights reserved.
**
** This file is part of TQt Linguist.
**
** This file may be used under the terms of the GNU General
** Public License versions 2.0 or 3.0 as published by the Free
** Software Foundation and appearing in the files LICENSE.GPL2
** and LICENSE.GPL3 included in the packaging of this file.
** Alternatively you may (at your option) use any later version
** of the GNU General Public License if such license has been
** publicly approved by Trolltech ASA (or its successors, if any)
** and the KDE Free TQt Foundation.
**
** Please review the following information to ensure GNU General
** Public Licensing requirements will be met:
** http://trolltech.com/products/qt/licenses/licensing/opensource/.
** If you are unsure which license is appropriate for your use, please
** review the following information:
** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
** or contact the sales department at sales@trolltech.com.
**
** Licensees holding valid TQt Commercial licenses may use this file in
** accordance with the TQt Commercial License Agreement provided with
** the Software.
**
** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
** herein.
**
**********************************************************************/

/*  TRANSLATOR TrWindow

  This is the application's main window.
*/

#include "trwindow.h"
#include "listviews.h"
#include "finddialog.h"
#include "msgedit.h"
#include "phrasebookbox.h"
#include "printout.h"
#include "about.h"
#include "phraselv.h"
#include "statistics.h"

#include <tqaccel.h>
#include <tqaction.h>
#include <tqapplication.h>
#include <tqbitmap.h>
#include <tqdict.h>
#include <tqdockarea.h>
#include <tqdockwindow.h>
#include <tqfile.h>
#include <tqfiledialog.h>
#include <tqfileinfo.h>
#include <tqheader.h>
#include <tqlabel.h>
#include <tqlayout.h>
#include <tqmenubar.h>
#include <tqmessagebox.h>
#include <tqpopupmenu.h>
#include <tqregexp.h>
#include <tqsettings.h>
#include <tqstatusbar.h>
#include <tqtoolbar.h>
#include <tqwhatsthis.h>
#include <tqprocess.h>
#include <tqobject.h>
#include <tqassistantclient.h>

#include <stdlib.h>

#define pagecurl_mask_width 53
#define pagecurl_mask_height 51
static const uchar pagecurl_mask_bits[] = {
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
   0xff, 0x0f, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0xfe, 0xff,
   0xff, 0xff, 0xff, 0x0f, 0x00, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00,
   0xc0, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff,
   0x0f, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0xfe, 0xff,
   0xff, 0xff, 0x0f, 0x00, 0x00, 0xfc, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00,
   0xfc, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0x0f,
   0x00, 0x00, 0xf0, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0xf0, 0xff, 0xff,
   0xff, 0x0f, 0x00, 0x00, 0xf0, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0xf0,
   0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff, 0x0f, 0x00,
   0x00, 0xe0, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff,
   0x0f, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0xe0, 0xff,
   0xff, 0xff, 0x0f, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00,
   0xe0, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff, 0x0f,
   0x00, 0x00, 0xe0, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0xe0, 0xff, 0xff,
   0xff, 0x0f, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0xe0,
   0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff, 0x0f, 0x00,
   0x00, 0xe0, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff,
   0x0f, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0xfc,
   0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0x0f, 0x00, 0x00,
   0x00, 0x00, 0x00, 0xfc, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x0f,
   0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00,
   0xe0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x0f, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08 };

typedef TQValueList<MetaTranslatorMessage> TML;

static const int ErrorMS = 600000; // for error messages
static const int MessageMS = 2500;

TQPixmap * TrWindow::pxOn = 0;
TQPixmap * TrWindow::pxOff = 0;
TQPixmap * TrWindow::pxObsolete = 0;
TQPixmap * TrWindow::pxDanger = 0;

enum Ending { End_None, End_FullStop, End_Interrobang, End_Colon,
	      End_Ellipsis };

static Ending ending( TQString str )
{
    str = str.simplifyWhiteSpace();
    int ch = 0;
    if ( !str.isEmpty() )
	ch = str.right( 1 )[0].tqunicode();

    switch ( ch ) {
    case 0x002e: // full stop
	if ( str.endsWith(TQString("...")) )
	    return End_Ellipsis;
	else
	    return End_FullStop;
    case 0x0589: // armenian full stop
    case 0x06d4: // arabic full stop
    case 0x3002: // ideographic full stop
	return End_FullStop;
    case 0x0021: // exclamation mark
    case 0x003f: // question mark
    case 0x00a1: // inverted exclamation mark
    case 0x00bf: // inverted question mark
    case 0x01c3: // latin letter retroflex click
    case 0x037e: // greek question mark
    case 0x061f: // arabic question mark
    case 0x203c: // double exclamation mark
    case 0x203d: // interrobang
    case 0x2048: // question exclamation mark
    case 0x2049: // exclamation question mark
    case 0x2762: // heavy exclamation mark ornament
	return End_Interrobang;
    case 0x003a: // colon
	return End_Colon;
    case 0x2026: // horizontal ellipsis
	return End_Ellipsis;
    default:
	return End_None;
    }
}

class Action : public TQAction
{
public:
    Action( TQPopupMenu *pop, const TQString& menuText, TQObject *receiver,
	    const char *member, const TQString &imageName = TQString(),
	    int accel = 0, bool toggle = FALSE );
    Action( TQPopupMenu *pop, const TQString& menuText, int accel = 0,
	    bool toggle = FALSE );

    virtual void setWhatsThis( const TQString& whatsThis );

    bool addToToolbar( TQToolBar *tb, const TQString& text );
};

Action::Action( TQPopupMenu *pop, const TQString& menuText, TQObject *receiver,
		const char *member, const TQString &imageName, int accel,
		bool toggle )
    : TQAction( pop->parent(), (const char *) 0, toggle )
{
    setMenuText( menuText );
    setAccel( accel );

    if ( !imageName.isEmpty() ) {
	TQPixmap enabledPix = TQPixmap::fromMimeSource( imageName );
	TQIconSet s( enabledPix );
	if ( imageName != "whatsthis.xpm" ) {
	    TQPixmap disabledPix = TQPixmap::fromMimeSource( "d_" + imageName );
	    s.setPixmap( disabledPix, TQIconSet::Small, TQIconSet::Disabled );
	}
	setIconSet( s );
    }
    TQAction::addTo( pop );
    connect( this, TQT_SIGNAL(activated()), receiver, member );
}

Action::Action( TQPopupMenu *pop, const TQString& menuText, int accel,
		bool toggle )
    : TQAction( pop->parent(), (const char *) 0, toggle )
{
    TQAction::addTo( pop );
    setMenuText( menuText );
    setAccel( accel );
}

void Action::setWhatsThis( const TQString& whatsThis )
{
    TQAction::setWhatsThis( whatsThis );
    setStatusTip( whatsThis );
}

bool Action::addToToolbar( TQToolBar *tb, const TQString& text )
{
    setText( text );
    return TQAction::addTo( tb );
}

const TQPixmap TrWindow::pageCurl()
{
    TQPixmap pixmap;
    pixmap = TQPixmap::fromMimeSource( "pagecurl.png" );
    if ( !pixmap.isNull() ) {
	TQBitmap pageCurlMask( pagecurl_mask_width, pagecurl_mask_height,
			pagecurl_mask_bits, TRUE );
	pixmap.setMask( pageCurlMask );
    }

    return pixmap;
}

TrWindow::TrWindow()
    : TQMainWindow( 0, "translation window", (WFlags)(TQt::WType_TopLevel | TQt::WDestructiveClose) )
{

#ifndef TQ_WS_MACX
    setIcon( TQPixmap::fromMimeSource( "appicon.png" ) );
#endif

    // Create the application global listview symbols
    pxOn  = new TQPixmap( TQPixmap::fromMimeSource( "s_check_on.png" ) );
    pxOff = new TQPixmap( TQPixmap::fromMimeSource( "s_check_off.png" ) );
    pxObsolete = new TQPixmap( TQPixmap::fromMimeSource( "d_s_check_obs.png" ) );
    pxDanger = new TQPixmap( TQPixmap::fromMimeSource( "s_check_danger.png" ) );

    // Set up the Scope dock window
    TQDockWindow * dwScope = new TQDockWindow( TQDockWindow::InDock, this,
					     "context");
    dwScope->setResizeEnabled( TRUE );
    dwScope->setCloseMode( TQDockWindow::Always );
    addDockWindow( dwScope, tqtr("Context"), TQt::DockLeft );
    dwScope->setCaption( tqtr("Context") );
    dwScope->setFixedExtentWidth( 200 );
    lv = new TQListView( dwScope, "context list view" );
    lv->setShowSortIndicator( TRUE );
    lv->setAllColumnsShowFocus( TRUE );
    lv->header()->setStretchEnabled( TRUE, 1 );
    TQFontMetrics fm( font() );
    lv->addColumn( tqtr("Done"), fm.width( tqtr("Done") ) + 10 );
    lv->addColumn( tqtr("Context") );
    lv->addColumn( tqtr("Items"), 55 );
    lv->setColumnAlignment( 0, TQt::AlignCenter );
    lv->setColumnAlignment( 2, TQt::AlignRight );
    lv->setSorting( 0 );
    lv->setHScrollBarMode( TQScrollView::AlwaysOff );
    dwScope->setWidget( lv );

    messageIsShown = FALSE;
    me = new MessageEditor( &tor, this, "message editor" );
    setCentralWidget( me );
    slv = me->sourceTextList();
    plv = me->phraseList();

    setupMenuBar();
    setupToolBars();

    progress = new TQLabel( statusBar(), "progress" );
    statusBar()->addWidget( progress, 0, TRUE );
    modified = new TQLabel( TQString(" %1 ").arg(tqtr("MOD")), statusBar(),
			   "modified?" );
    statusBar()->addWidget( modified, 0, TRUE );

    dirtyItem = -1;
    numFinished = 0;
    numNonobsolete = 0;
    numMessages = 0;
    updateProgress();

    dirty = FALSE;
    updateCaption();

    phraseBooks.setAutoDelete( TRUE );

    f = new FindDialog( FALSE, this, "find", FALSE );
    f->setCaption( tqtr("TQt Linguist") );
    h = new FindDialog( TRUE, this, "replace", FALSE );
    h->setCaption( tqtr("TQt Linguist") );
    findMatchCase = FALSE;
    findWhere = 0;
    foundItem = 0;
    foundScope = 0;
    foundWhere = 0;
    foundOffset = 0;

    connect( lv, TQT_SIGNAL(selectionChanged(TQListViewItem *)),
	     this, TQT_SLOT(showNewScope(TQListViewItem *)) );

    connect( slv, TQT_SIGNAL(currentChanged(TQListViewItem *)),
	     this, TQT_SLOT(showNewCurrent(TQListViewItem *)) );

    connect( slv, TQT_SIGNAL(clicked(TQListViewItem *, const TQPoint&, int)),
	     this, TQT_SLOT(showNewCurrent(TQListViewItem *)) );

    connect( slv, TQT_SIGNAL(clicked(TQListViewItem *, const TQPoint&, int)),
	     this, TQT_SLOT(toggleFinished(TQListViewItem *, const TQPoint&, int)) );

    connect( me, TQT_SIGNAL(translationChanged(const TQString&)),
	     this, TQT_SLOT(updateTranslation(const TQString&)) );
    connect( me, TQT_SIGNAL(finished(bool)), this, TQT_SLOT(updateFinished(bool)) );
    connect( me, TQT_SIGNAL(prevUnfinished()), this, TQT_SLOT(prevUnfinished()) );
    connect( me, TQT_SIGNAL(nextUnfinished()), this, TQT_SLOT(nextUnfinished()) );
    connect( me, TQT_SIGNAL(focusSourceList()), this, TQT_SLOT(focusSourceList()) );
    connect( me, TQT_SIGNAL(focusPhraseList()), this, TQT_SLOT(focusPhraseList()) );
    connect( f, TQT_SIGNAL(findNext(const TQString&, int, bool)),
	     this, TQT_SLOT(findNext(const TQString&, int, bool)) );

    TQWhatsThis::add( lv, tqtr("This panel lists the source contexts.") );

    TQWhatsThis::add( slv, tqtr("This panel lists the source texts. "
			    "Items that violate validation rules "
			    "are marked with a warning.") );
    showNewCurrent( 0 );

    TQSize as( tqApp->desktop()->size() );
    as -= TQSize( 30, 30 );
    resize( TQSize( 1000, 800 ).boundedTo( as ) );
    readConfig();
    stats = 0;
    srcWords = 0;
    srcChars = 0;
    srcCharsSpc = 0;
}

TrWindow::~TrWindow()
{
    writeConfig();
    delete stats;
}

void TrWindow::openFile( const TQString& name )
{
    if ( !name.isEmpty() ) {
	statusBar()->message( tqtr("Loading...") );
	tqApp->processEvents();
	tor.clear();
	if ( tor.load(name) ) {
	    slv->clear();
	    slv->tqrepaint();
	    slv->viewport()->tqrepaint();
	    slv->setUpdatesEnabled( FALSE );
	    slv->viewport()->setUpdatesEnabled( FALSE );
	    lv->clear();
	    lv->tqrepaint();
	    lv->viewport()->tqrepaint();
	    lv->setUpdatesEnabled( FALSE );
	    lv->viewport()->setUpdatesEnabled( FALSE );
	    setEnabled( FALSE );
	    numFinished = 0;
	    numNonobsolete = 0;
	    numMessages = 0;
	    foundScope = 0;

	    TML all = tor.messages();
	    TML::Iterator it;
	    TQDict<ContextLVI> contexts( 1009 );

	    srcWords = 0;
	    srcChars = 0;
	    srcCharsSpc = 0;
	    for ( it = all.begin(); it != all.end(); ++it ) {
		tqApp->processEvents();
		ContextLVI *c = contexts.find( TQString((*it).context()) );
		if ( c == 0 ) {
		    c = new ContextLVI( lv, tor.toUnicode((*it).context(),
							  (*it).utf8()) );
		    contexts.insert( TQString((*it).context()), c );
		}
		if ( TQCString((*it).sourceText()) == ContextComment ) {
		    c->appendToComment( tor.toUnicode((*it).comment(),
						      (*it).utf8()) );
		} else {
		    MessageLVI * tmp = new MessageLVI( slv, *it,
					   tor.toUnicode((*it).sourceText(),
							 (*it).utf8()),
					   tor.toUnicode((*it).comment(),
							 (*it).utf8()), c );
		    tmp->setDanger( danger(tmp->sourceText(),
					   tmp->translation()) &&
				    tmp->message().type() ==
				    MetaTranslatorMessage::Finished );
		    c->instantiateMessageItem( slv, tmp );
		    if ( (*it).type() != MetaTranslatorMessage::Obsolete ) {
			numNonobsolete++;
			if ( (*it).type() == MetaTranslatorMessage::Finished )
			    numFinished++;
			doCharCounting( tmp->sourceText(), srcWords, srcChars, srcCharsSpc );
		    } else {
			c->incrementObsoleteCount();
		    }
		    numMessages++;
		}
		c->updatetqStatus();
	    }
	    slv->viewport()->setUpdatesEnabled( TRUE );
	    slv->setUpdatesEnabled( TRUE );
	    lv->viewport()->setUpdatesEnabled( TRUE );
	    lv->setUpdatesEnabled( TRUE );
	    setEnabled( TRUE );
	    slv->tqrepaint();
	    slv->viewport()->tqrepaint();
	    lv->triggerUpdate();
	    updateProgress();
	    filename = name;
	    dirty = FALSE;
	    updateCaption();
	    me->showNothing();
	    doneAndNextAct->setEnabled( FALSE );
	    doneAndNextAlt->setEnabled( FALSE );
	    messageIsShown = FALSE;
	    statusBar()->message(
		    tqtr("%1 source phrase(s) loaded.").arg(numMessages),
		    MessageMS );

	    foundItem = 0;
	    foundWhere = 0;
	    foundOffset = 0;
	    if ( lv->childCount() > 0 ) {
		findAct->setEnabled( TRUE );
		findAgainAct->setEnabled( FALSE );
#ifdef notyet
		replaceAct->setEnabled( TRUE );
#endif
		lv->setCurrentItem( lv->firstChild() );
	    }
	    addRecentlyOpenedFile( name, recentFiles );
	    updateStatistics();
	} else {
	    statusBar()->clear();
	    TQMessageBox::warning( this, tqtr("TQt Linguist"),
				  tqtr("Cannot open '%1'.").arg(name) );
	}
    }
}

void TrWindow::open()
{
    if ( maybeSave() ) {
	TQString newFilename = TQFileDialog::getOpenFileName( filename,
		tqtr("TQt translation source (*.ts)\n"
		   "All files (*)"),
		this, "open" );
	openFile( newFilename );
    }
}

void TrWindow::save()
{
    if ( filename.isEmpty() )
	return;

    if ( tor.save(filename) ) {
	dirty = FALSE;
	updateCaption();
	statusBar()->message( tqtr("File saved."), MessageMS );
    } else {
	TQMessageBox::warning( this, tqtr("TQt Linguist"), tqtr("Cannot save '%1'.")
			      .arg(filename) );
    }
}

void TrWindow::saveAs()
{
    TQString newFilename = TQFileDialog::getSaveFileName( filename,
	    tqtr( "TQt translation source (*.ts)\n"
		"All files (*)"),
	    this, "save_as" );
    if ( !newFilename.isEmpty() ) {
	filename = newFilename;
	save();
	updateCaption();
    }
}

void TrWindow::release()
{
    TQString newFilename = filename;
    newFilename.replace( TQRegExp(".ts$"), "" );
    newFilename += TQString( ".qm" );

    newFilename = TQFileDialog::getSaveFileName( newFilename,
	    tqtr("TQt message files for released applications (*.qm)\n"
	       "All files (*)"),
	    this, "release",
	    tqtr("Release") );
    if ( !newFilename.isEmpty() ) {
	if ( tor.release(newFilename) )
	    statusBar()->message( tqtr("File created."), MessageMS );
	else
	    TQMessageBox::warning( this, tqtr("TQt Linguist"),
				  tqtr("Cannot save '%1'.").arg(newFilename) );
    }
}

void TrWindow::print()
{
    int pageNum = 0;

    if ( printer.setup(this) ) {
	TQApplication::setOverrideCursor( WaitCursor );
	printer.setDocName( filename );
	statusBar()->message( tqtr("Printing...") );
	PrintOut pout( &printer );
	ContextLVI *c = (ContextLVI *) lv->firstChild();
	while ( c != 0 ) {
	    setCurrentContextItem( c );
	    pout.vskip();
	    pout.setRule( PrintOut::ThickRule );
	    pout.setGuide( c->context() );
	    pout.addBox( 100, tqtr("Context: %1").arg(c->context()),
			 PrintOut::Strong );
	    pout.flushLine();
	    pout.addBox( 4 );
	    pout.addBox( 92, c->comment(), PrintOut::Emphasis );
	    pout.flushLine();
	    pout.setRule( PrintOut::ThickRule );

	    MessageLVI *m = (MessageLVI *) slv->firstChild();
	    while ( m != 0 ) {
		pout.setRule( PrintOut::ThinRule );

		TQString type;
		switch ( m->message().type() ) {
		case MetaTranslatorMessage::Finished:
		    type = tqtr( "finished" );
		    break;
		case MetaTranslatorMessage::Unfinished:
		    type = m->danger() ? tqtr( "unresolved" ) : TQString( "unfinished" );
		    break;
		case MetaTranslatorMessage::Obsolete:
		    type = tqtr( "obsolete" );
		    break;
		default:
		    type = TQString( "" );
		}
		pout.addBox( 40, m->sourceText() );
		pout.addBox( 4 );
		pout.addBox( 40, m->translation() );
		pout.addBox( 4 );
		pout.addBox( 12, type, PrintOut::Normal, TQt::AlignRight );
		if ( !m->comment().isEmpty() ) {
		    pout.flushLine();
		    pout.addBox( 4 );
		    pout.addBox( 92, m->comment(), PrintOut::Emphasis );
		}
		pout.flushLine( TRUE );

		if ( pout.pageNum() != pageNum ) {
		    pageNum = pout.pageNum();
		    statusBar()->message( tqtr("Printing... (page %1)")
					  .arg(pageNum) );
		}
		m = (MessageLVI *) m->nextSibling();
	    }
	    c = (ContextLVI *) c->nextSibling();
	}
	pout.flushLine( TRUE );
	TQApplication::restoreOverrideCursor();
	statusBar()->message( tqtr("Printing completed"), MessageMS );
    } else {
	statusBar()->message( tqtr("Printing aborted"), MessageMS );
    }
}

void TrWindow::find()
{
    h->hide();
    f->show();
    f->setActiveWindow();
    f->raise();
}

void TrWindow::findAgain()
{
    int pass = 0;
    int oldItemNo = itemToIndex( slv, slv->currentItem() );
    TQString delayedMsg;
    TQListViewItem * j = foundScope;
    TQListViewItem * k = indexToItem( slv, foundItem );
    TQListViewItem * oldScope = lv->currentItem();

    if ( lv->childCount() == 0 )
	return;
#if 1
    /*
      As long as we don't implement highlighting of the text in the TQTextView,
      we may have only one match per message.
    */
    foundOffset = (int) 0x7fffffff;
#else
    foundOffset++;
#endif
    slv->setUpdatesEnabled( FALSE );
    do {
	// Iterate through every item in all contexts
	if ( j == 0 ) {
	    j = lv->firstChild();
	    setCurrentContextItem( j );
	    if ( foundScope != 0 )
		delayedMsg = tqtr("Search wrapped.");
	}
	if ( k == 0 )
	    k = slv->firstChild();

	while ( k ) {
	    MessageLVI * m = (MessageLVI *) k;
	    switch ( foundWhere ) {
		case 0:
		    foundWhere  = FindDialog::SourceText;
		    foundOffset = 0;
		    // fall-through
		case FindDialog::SourceText:
		    if ( searchItem( m->sourceText(), j, k ) ) {
			f->hide();
			if ( !delayedMsg.isEmpty() )
			    statusBar()->message( delayedMsg, MessageMS );
 			return;
		    }
		    foundWhere  = FindDialog::Translations;
		    foundOffset = 0;
		    // fall-through
		case FindDialog::Translations:
		    if ( searchItem( m->translation(), j, k ) ) {
			f->hide();
			if ( !delayedMsg.isEmpty() )
			    statusBar()->message( delayedMsg, MessageMS );
			return;
		    }
		    foundWhere  = FindDialog::Comments;
		    foundOffset = 0;
		    // fall-through
		case FindDialog::Comments:
		    if ( searchItem( ((ContextLVI *) j)->fullContext(), j, k) ) {
			f->hide();
			if ( !delayedMsg.isEmpty() )
			    statusBar()->message( delayedMsg, MessageMS );
			return;
		    }
		    foundWhere  = 0;
		    foundOffset = 0;
	    }
	    k = k->nextSibling();
	}

	j = j->nextSibling();
	if ( j ) {
	    setCurrentContextItem( j );
	    k = slv->firstChild();
	}
    } while ( pass++ != lv->childCount() );

    // This is just to keep the current scope and source text item
    // selected after a search failed.
    if ( oldScope ) {
	setCurrentContextItem( oldScope );
	TQListViewItem * tmp = indexToItem( slv, oldItemNo );
	if( tmp )
	    setCurrentMessageItem( tmp );
    } else {
	if( lv->firstChild() )
	    setCurrentContextItem( lv->firstChild() );
	if( slv->firstChild() )
	    setCurrentMessageItem( slv->firstChild() );
    }

    slv->setUpdatesEnabled( TRUE );
    slv->triggerUpdate();
    tqApp->beep();
    TQMessageBox::warning( this, tqtr("TQt Linguist"),
			  TQString( tqtr("Cannot find the string '%1'.") ).arg(findText) );
    foundItem   = 0;
    foundWhere  = 0;
    foundOffset = 0;
}

void TrWindow::replace()
{
    f->hide();
    h->show();
    h->setActiveWindow();
    h->raise();
}

int TrWindow::itemToIndex( TQListView * view, TQListViewItem * item )
{
    int no = 0;
    TQListViewItem * tmp;

    if( view && item ){
	if( (tmp = view->firstChild()) != 0 )
	    do {
		no++;
		tmp = tmp->nextSibling();
	    } while( tmp && (tmp != item) );
    }
    return no;
}

TQListViewItem * TrWindow::indexToItem( TQListView * view, int index )
{
    TQListViewItem * item = 0;

    if ( view && index > 0 ) {
	item = view->firstChild();
	while( item && index-- > 0 )
	    item = item->nextSibling();
    }
    return item;
}

bool TrWindow::searchItem( const TQString & searchWhat, TQListViewItem * j,
			   TQListViewItem * k )
{
    if ( (findWhere & foundWhere) != 0 ) {
	foundOffset = searchWhat.find( findText, foundOffset, findMatchCase );
	if ( foundOffset >= 0 ) {
	    foundItem = itemToIndex( slv, k );
	    foundScope = j;
	    setCurrentMessageItem( k );
	    slv->setUpdatesEnabled( TRUE );
	    slv->triggerUpdate();
	    return TRUE;
	}
    }
    foundOffset = 0;
    return FALSE;
}

void TrWindow::newPhraseBook()
{
    TQString name;
    for (;;) {
	name = TQFileDialog::getSaveFileName( TQString(),
	    tqtr("TQt phrase books (*.qph)\n"
	       "All files (*)"),
	    this, "new_phrasebook",
	    tqtr("Create New Phrase Book") );
	if ( !TQFile::exists(name) )
	    break;
	TQMessageBox::warning( this, tqtr("TQt Linguist"),
			      tqtr("A file called '%1' already exists."
				 "  Please choose another name.").arg(name) );
    }
    if ( !name.isEmpty() ) {
	PhraseBook pb;
	if ( savePhraseBook(name, pb) ) {
	    if ( openPhraseBook(name) )
		statusBar()->message( tqtr("Phrase book created."), MessageMS );
	}
    }
}

void TrWindow::openPhraseBook()
{
    TQString phrasebooks( qInstallPathData() );
    TQString name = TQFileDialog::getOpenFileName( phrasebooks + "/phrasebooks",
	tqtr("TQt phrase books (*.qph)\n"
	   "All files (*)"),
	this, "open_phrasebook",
	tqtr("Open Phrase Book") );
    if ( !name.isEmpty() && !phraseBookNames.contains(name) ) {
	if ( openPhraseBook(name) ) {
	    int n = (int)(phraseBooks.at( phraseBooks.count() - 1 )->count());
	    statusBar()->message( tqtr("%1 phrase(s) loaded.").arg(n),
				  MessageMS );
	}
    }
}

void TrWindow::closePhraseBook( int id )
{
    int index = closePhraseBookp->indexOf( id );
    phraseBooks.remove( index );
    phraseBookNames.remove( phraseBookNames.at(index) );
    updatePhraseDict();

    dirtyItem = index; // remove the item next time the menu is opened
    editPhraseBookp->removeItem( editPhraseBookp->idAt(index) );
    printPhraseBookp->removeItem( printPhraseBookp->idAt(index) );
}

void TrWindow::editPhraseBook( int id )
{
    int index = editPhraseBookp->indexOf( id );
    PhraseBookBox box( phraseBookNames[index], *phraseBooks.at(index), this,
		       "phrase book box", TRUE );
    box.setCaption( tqtr("%1 - %2").arg(tqtr("TQt Linguist"))
				 .arg(friendlyPhraseBookName(index)) );
    box.resize( 500, 300 );
    box.exec();
    *phraseBooks.at( index ) = box.phraseBook();
    updatePhraseDict();
}

void TrWindow::printPhraseBook( int id )
{
    int index = printPhraseBookp->indexOf( id );
    int pageNum = 0;

    if ( printer.setup(this) ) {
	printer.setDocName( phraseBookNames[index] );
	statusBar()->message( tqtr("Printing...") );
	PrintOut pout( &printer );
	PhraseBook *phraseBook = phraseBooks.at( index );
	PhraseBook::Iterator p;
	pout.setRule( PrintOut::ThinRule );
	for ( p = phraseBook->begin(); p != phraseBook->end(); ++p ) {
	    pout.setGuide( (*p).source() );
	    pout.addBox( 29, (*p).source() );
	    pout.addBox( 4 );
	    pout.addBox( 29, (*p).target() );
	    pout.addBox( 4 );
	    pout.addBox( 34, (*p).definition(), PrintOut::Emphasis );

	    if ( pout.pageNum() != pageNum ) {
		pageNum = pout.pageNum();
		statusBar()->message( tqtr("Printing... (page %1)")
				      .arg(pageNum) );
	    }
	    pout.setRule( PrintOut::NoRule );
	    pout.flushLine( TRUE );
	}
	pout.flushLine( TRUE );
	statusBar()->message( tqtr("Printing completed"), MessageMS );
    } else {
	statusBar()->message( tqtr("Printing aborted"), MessageMS );
    }
}

void TrWindow::revertSorting()
{
    lv->setSorting( 0 );
    slv->setSorting( 0 );
}

void TrWindow::manual()
{
    TQAssistantClient *ac = new TQAssistantClient( qInstallPathBins(), TQT_TQOBJECT(this) );
    ac->showPage( TQString( qInstallPathDocs() ) + "/html/linguist-manual.html" );
}

void TrWindow::about()
{
    AboutDialog about( this, 0, TRUE );
    about.versionLabel->setText( tqtr("Version %1").arg(TQT_VERSION_STR) );
    about.exec();
}

void TrWindow::aboutTQt()
{
    TQMessageBox::aboutTQt( this, tqtr("TQt Linguist") );
}

void TrWindow::setupPhrase()
{
    bool enabled = !phraseBooks.isEmpty();
    phrasep->setItemEnabled( closePhraseBookId, enabled );
    phrasep->setItemEnabled( editPhraseBookId, enabled );
    phrasep->setItemEnabled( printPhraseBookId, enabled );
}

void TrWindow::closeEvent( TQCloseEvent *e )
{
    if ( maybeSave() )
	e->accept();
    else
	e->ignore();
}

bool TrWindow::maybeSave()
{
    if ( dirty ) {
	switch ( TQMessageBox::information(this, tqtr("TQt Linguist"),
				  tqtr("Do you want to save '%1'?")
				  .arg(filename),
				  TQMessageBox::Yes | TQMessageBox::Default,
				  TQMessageBox::No,
				  TQMessageBox::Cancel | TQMessageBox::Escape ) )
	{
	    case TQMessageBox::Cancel:
		return FALSE;
	    case TQMessageBox::Yes:
		save();
                return !dirty;
	    case TQMessageBox::No:
		break;
	}
    }
    return TRUE;
}

void TrWindow::updateCaption()
{
    TQString cap;
    bool enable = !filename.isEmpty();
    saveAct->setEnabled( enable );
    saveAsAct->setEnabled( enable );
    releaseAct->setEnabled( enable );
    printAct->setEnabled( enable );
    acceleratorsAct->setEnabled( enable );
    endingPunctuationAct->setEnabled( enable );
    phraseMatchesAct->setEnabled( enable );
    revertSortingAct->setEnabled( enable );

    if ( filename.isEmpty() )
	cap = tqtr( "TQt Linguist by Trolltech" );
    else
	cap = tqtr( "%1 - %2" ).arg( tqtr("TQt Linguist by Trolltech") )
			     .arg( filename );
    setCaption( cap );
    modified->setEnabled( dirty );
}

//
// New scope selected - build a new list of source text items
// for that scope.
//
void TrWindow::showNewScope( TQListViewItem *item )
{
    static ContextLVI * oldContext = 0;

    if( item != 0 ) {
	ContextLVI * c = (ContextLVI *) item;
	bool upe = slv->isUpdatesEnabled();
	slv->setUpdatesEnabled( FALSE );
	slv->viewport()->setUpdatesEnabled( FALSE );
	if ( oldContext != 0 ) {
	    MessageLVI * tmp;
	    slv->blockSignals( TRUE );
	    while ( (tmp = (MessageLVI *) slv->firstChild()) != 0 )
		oldContext->appendMessageItem( slv, tmp );
	    slv->blockSignals( FALSE );
	}
	MessageLVI * tmp;
	while ( c->messageItemsInList() ) {
	    tmp = c->takeMessageItem( c->messageItemsInList() - 1);
	    slv->insertItem( tmp );
	    tmp->updateTranslationText();
	}

	// Make sure that we update the source text and translation text. Use the first message in the new scope.
	setCurrentMessageItem(slv->firstChild());
	// This must be explicitly called since the signal is not emitted if the context only contains one item
	showNewCurrent(slv->firstChild());
	slv->viewport()->setUpdatesEnabled( upe );
	slv->setUpdatesEnabled( upe );
	if( upe )
	    slv->triggerUpdate();
	oldContext = (ContextLVI *) item;
	statusBar()->clear();
    }
}

void TrWindow::showNewCurrent( TQListViewItem *item )
{
    messageIsShown = (item != 0);
    MessageLVI *m = (MessageLVI *) item;
    ContextLVI *c = (ContextLVI *) m ? m->contextLVI() : 0;

    if ( messageIsShown ) {
	me->showMessage( m->sourceText(), m->comment(), c->fullContext(),
			 m->translation(), m->message().type(),
			 getPhrases(m->sourceText()) );
	if ( (m->message().type() != MetaTranslatorMessage::Finished) &&
	     m->danger() )
	    danger( m->sourceText(), m->translation(), TRUE );
	else
	    statusBar()->clear();

	doneAndNextAct->setEnabled( m->message().type() !=
				    MetaTranslatorMessage::Obsolete );
    } else {
	if ( item == 0 )
	    me->showNothing();
	else
	    me->showContext( c->fullContext(), c->finished() );
	doneAndNextAct->setEnabled( FALSE );
    }
    doneAndNextAlt->setEnabled( doneAndNextAct->isEnabled() );

    selectAllAct->setEnabled( messageIsShown );
}

void TrWindow::updateTranslation( const TQString& translation )
{
    TQListViewItem *item = slv->currentItem();
    if ( item != 0 ) {
	MessageLVI *m = (MessageLVI *) item;
	if ( translation != m->translation() ) {
	    bool dngr;
	    m->setTranslation( translation );
	    if ( m->finished() &&
		 (dngr = danger( m->sourceText(), m->translation(), TRUE )) ) {
		numFinished -= 1;
		m->setDanger( dngr );
		m->setFinished( FALSE );
		m->contextLVI()->updatetqStatus();
		updateProgress();
	    }
	    tor.insert( m->message() );
	    if ( !dirty ) {
		dirty = TRUE;
		updateCaption();
	    }
	    m->updateTranslationText();
	}
    }
}

void TrWindow::updateFinished( bool finished )
{
    TQListViewItem *item = slv->currentItem();
    if ( item != 0 ) {
	MessageLVI *m = (MessageLVI *) item;
	if ( finished != m->finished() ) {
	    numFinished += finished ? +1 : -1;
	    updateProgress();
	    m->setFinished( finished );
	    bool oldDanger = m->danger();
	    m->setDanger( /*m->finished() &&*/
			  danger(m->sourceText(), m->translation(),
			  !oldDanger) );
	    if ( !oldDanger && m->danger() )
		tqApp->beep();
	    tor.insert( m->message() );
	    if ( !dirty ) {
		dirty = TRUE;
		updateCaption();
	    }
	}
    }
}

void TrWindow::doneAndNext()
{
    MessageLVI * m = (MessageLVI *) slv->currentItem();
    bool dngr = FALSE;

    if ( !m ) return;
    dngr = danger( m->sourceText(), m->translation(), TRUE );
    if ( !dngr ) {
	me->finishAndNext();
	m->contextLVI()->updatetqStatus();
    } else {
	if ( m->danger() != dngr )
	    m->setDanger( dngr );
	tor.insert( m->message() );
	if ( !dirty ) {
	    dirty = TRUE;
	    updateCaption();
	}
	tqApp->beep();
    }
    updateStatistics();
}

void TrWindow::toggleFinished( TQListViewItem *item, const TQPoint& /* p */,
			       int column )
{
    if ( item != 0 && column == 0 ) {
	MessageLVI *m = (MessageLVI *) item;
	bool dngr = FALSE;

	if ( m->message().type() == MetaTranslatorMessage::Unfinished ) {
	    dngr = danger( m->sourceText(), m->translation(), TRUE );
	}
	if ( !dngr && m->message().type() != MetaTranslatorMessage::Obsolete) {
	    setCurrentMessageItem( m );
	    me->setFinished( !m->finished() );
	    m->contextLVI()->updatetqStatus();
	} else {
	    bool oldDanger = m->danger();
	    m->setDanger( danger(m->sourceText(), m->translation(),
				 !oldDanger) );
	    if ( !oldDanger && m->danger() )
		tqApp->beep();
	    tor.insert( m->message() );
	    if ( !dirty ) {
		dirty = TRUE;
		updateCaption();
	    }
	}
	updateStatistics();
    }
}

void TrWindow::nextUnfinished()
{
    if ( nextUnfinishedAct->isEnabled() ) {
	// Select a message to translate, grab the first available if
	// there are no current selection.
	TQListViewItem * cItem = lv->currentItem(); // context item
	TQListViewItem * mItem = slv->currentItem(); // message item

	// Make sure an item is selected from both the context and the
	// message list.
	if( (mItem == 0) && !(mItem = slv->firstChild()) ) {
	    if( (cItem == 0) && !(cItem = lv->firstChild()) ) {
		statusBar()->message( tqtr("No phrase to translate."),
				      MessageMS );
		tqApp->beep();
		return;
	    } else {
		showNewScope( cItem );
		while( cItem && !(mItem = slv->firstChild()) ) {
		    // no tqchildren in this node - try next one
		    cItem = cItem->nextSibling();
		    showNewScope( cItem );
		}
		setCurrentContextItem( cItem );
		if( mItem ) {
		    setCurrentMessageItem( mItem );
		} else {
		    statusBar()->message( tqtr("No phrase to translate."),
					  MessageMS );
		    tqApp->beep();
		    return;
		}
	    }
	} else {
	    setCurrentMessageItem( mItem );
	}

	MessageLVI * m = (MessageLVI *) mItem;
	MessageLVI * n;
	ContextLVI * p = (ContextLVI *) cItem;
	ContextLVI * q;

	// Find the next Unfinished sibling within the same context.
	m = (MessageLVI *) mItem->nextSibling();
	n = m;
	do {
	    if ( n == 0 )
		break;
	    if ( n && !n->finished() && n != mItem ) {
		setCurrentMessageItem( n );
		return;
	    }
	    n = (MessageLVI *) n->nextSibling();
	} while ( n != m );

	// If all siblings are Finished or Obsolete, look in the first
	// Unfinished context.
	p = (ContextLVI *) p->nextSibling();
	q = p;
	do {
	    if ( q == 0 )
		q = (ContextLVI *) lv->firstChild();
	    if ( q && !q->finished() ) {
		showNewScope( q );
		setCurrentContextItem( q );
		n = (MessageLVI *) slv->firstChild();
		while ( n && n->finished() )
		    n = (MessageLVI *) n->nextSibling();
		if ( n && q ) {
		    setCurrentMessageItem( n );
		    showNewCurrent( n );
		    return;
		}
	    }
	    q = (ContextLVI *) q->nextSibling();
	} while ( q != p );
    }

    // If no Unfinished message is left, the user has finished the job.  We
    // congratulate on a job well done with this ringing bell.
    statusBar()->message( tqtr("No untranslated phrases left."), MessageMS );
    tqApp->beep();
}

static TQListViewItem * lastChild( TQListView * view )
{
    if ( view ) {
	TQListViewItem * ret, * tmp;
	ret = view->firstChild();
	while ( ret ) {
	    tmp = ret->nextSibling();
	    if ( tmp == 0 )
		return ret;
	    ret = tmp;
	}
    }
    return 0;
}

void TrWindow::prevUnfinished()
{
    if ( prevUnfinishedAct->isEnabled() ) {
	// Select a message to translate, grab the first available if
	// there are no current selection.
	TQListViewItem * cItem = lv->currentItem();  // context item
	TQListViewItem * mItem = slv->currentItem(); // message item

	// Make sure an item is selected from both the context and the
	// message list.
	if( (mItem == 0) && !(mItem = slv->firstChild()) ) {
	    if( (cItem == 0) && !(cItem = lv->firstChild()) ) {
		statusBar()->message( tqtr("No phrase to translate."),
				      MessageMS );
		tqApp->beep();
		return;
	    } else {
		showNewScope( cItem );
		while( cItem && !(mItem = slv->firstChild()) ) {
		    // no tqchildren in this node - try next one
		    cItem = cItem->nextSibling();
		    showNewScope( cItem );
		}
		setCurrentContextItem( cItem );
		if( mItem ) {
		    setCurrentMessageItem( cItem );
		} else {
		    statusBar()->message( tqtr("No phrase to translate."),
					  MessageMS );
		    tqApp->beep();
		    return;
		}
	    }
	} else {
	    setCurrentMessageItem( mItem );
	}

	MessageLVI * m = (MessageLVI *) mItem;
	MessageLVI * n;
	ContextLVI * p = (ContextLVI *) cItem;
	ContextLVI * q;

	// Find the next Unfinished sibling within the same context.
	n = m;
	do {
	    n = (MessageLVI * ) n->itemAbove();
	    if ( n == 0 )
		break;
	    if ( n && !n->finished() ) {
		setCurrentMessageItem( n );
		return;
	    }
	} while ( !((ContextLVI *) cItem)->finished() && n != 0 );

	// If all siblings are Finished or Obsolete, look in the prev
	// Unfinished context.
	q = p;
	do {
	    q = (ContextLVI *) q->itemAbove();
	    if ( q == 0 )
		q = (ContextLVI *) lastChild( lv );
	    if ( q && !q->finished() ) {
		showNewScope( q );
		setCurrentContextItem( q );
		n = (MessageLVI *) lastChild( slv );
		while ( n && n->finished() )
		    n = (MessageLVI *) n->itemAbove();
		if ( n && q ) {
		    setCurrentMessageItem( n );
		    return;
		}
	    }
	} while ( q != 0 );
    }
    statusBar()->message( tqtr("No untranslated phrases left."), MessageMS );
    tqApp->beep();
}

void TrWindow::prev()
{
    TQListViewItem * cItem = lv->currentItem();  // context item
    TQListViewItem * mItem = slv->currentItem(); // message item
    TQListViewItem * tmp;

    if ( !cItem ) {
	cItem = lv->firstChild();
	if ( !cItem ) return;
	setCurrentContextItem( cItem );
    }

    if ( !mItem ) {
	mItem = lastChild( slv );
	if ( !mItem ) return;
	setCurrentMessageItem( mItem );
    } else {
	if ( (tmp = mItem->itemAbove()) != 0 ) {
	    setCurrentMessageItem( tmp );
	    return;
	} else {
	    if ( (tmp = cItem->itemAbove()) == 0 ) {
		tmp = lastChild( lv );
	    }
	    if ( !tmp ) return;
	    setCurrentContextItem( tmp );
	    setCurrentMessageItem( lastChild( slv ) );
	}
    }
}

void TrWindow::next()
{
    TQListViewItem * cItem = lv->currentItem();  // context item
    TQListViewItem * mItem = slv->currentItem(); // message item
    TQListViewItem * tmp;

    if ( !cItem ) {
	cItem = lv->firstChild();
	if ( !cItem ) return;
	setCurrentContextItem( cItem );
    }

    if ( !mItem ) {
	mItem = slv->firstChild();
	if ( !mItem ) return;
	setCurrentMessageItem( mItem );
    } else {
	if ( (tmp = mItem->nextSibling()) != 0 ) {
	    setCurrentMessageItem( tmp );
	    return;
	} else {
	    if ( (tmp = cItem->nextSibling()) == 0 ) {
		tmp = lv->firstChild();
	    }
	    if ( !tmp ) return;
	    setCurrentContextItem( tmp );
	    setCurrentMessageItem( slv->firstChild() );
	}
    }
}


void TrWindow::findNext( const TQString& text, int where, bool matchCase )
{
    findText = text;
    if ( findText.isEmpty() )
	findText = TQString( "magicwordthatyoushouldavoid" );
    findWhere = where;
    findMatchCase = matchCase;
    findAgainAct->setEnabled( TRUE );
    findAgain();
}

void TrWindow::revalidate()
{
    ContextLVI *c = (ContextLVI *) lv->firstChild();
    TQListViewItem * oldScope = lv->currentItem();
    int oldItemNo = itemToIndex( slv, slv->currentItem() );
    slv->setUpdatesEnabled( FALSE );

    while ( c != 0 ) {
	showNewScope( c );
	MessageLVI *m = (MessageLVI *) slv->firstChild();
	while ( m != 0 ) {
	    m->setDanger( danger(m->sourceText(), m->translation()) &&
		    m->message().type() == MetaTranslatorMessage::Finished );
	    m = (MessageLVI *) m->nextSibling();
	}
	c = (ContextLVI *) c->nextSibling();
    }

    if ( oldScope ){
	showNewScope( oldScope );
	TQListViewItem * tmp = indexToItem( slv, oldItemNo );
	if( tmp )
	    setCurrentMessageItem( tmp );
    }
    slv->setUpdatesEnabled( TRUE );
    slv->triggerUpdate();
}

TQString TrWindow::friendlyString( const TQString& str )
{
    TQString f = str.lower();
    f.replace( TQRegExp(TQString("[.,:;!?()-]")), TQString(" ") );
    f.replace( "&", TQString("") );
    f = f.simplifyWhiteSpace();
    f = f.lower();
    return f;
}

void TrWindow::setupMenuBar()
{
    TQMenuBar * m = menuBar();
    TQPopupMenu * filep = new TQPopupMenu( this );
    TQPopupMenu * editp  = new TQPopupMenu( this );
    TQPopupMenu * translationp = new TQPopupMenu( this );
    TQPopupMenu * validationp = new TQPopupMenu( this );
    validationp->setCheckable( TRUE );
    phrasep = new TQPopupMenu( this );
    closePhraseBookp = new TQPopupMenu( this );
    editPhraseBookp = new TQPopupMenu( this );
    printPhraseBookp = new TQPopupMenu( this );
    TQPopupMenu * viewp = new TQPopupMenu( this );
    viewp->setCheckable( TRUE );
    TQPopupMenu * helpp = new TQPopupMenu( this );

    m->insertItem( tqtr("&File"), filep );
    m->insertItem( tqtr("&Edit"), editp );
    m->insertItem( tqtr("&Translation"), translationp );
    m->insertItem( tqtr("V&alidation"), validationp );
    m->insertItem( tqtr("&Phrases"), phrasep );
    m->insertItem( tqtr("&View"), viewp );
    m->insertSeparator();
    m->insertItem( tqtr("&Help"), helpp );

    connect( closePhraseBookp, TQT_SIGNAL(activated(int)),
	     this, TQT_SLOT(closePhraseBook(int)) );
    connect( closePhraseBookp, TQT_SIGNAL(aboutToShow()),
	     this, TQT_SLOT(updateClosePhraseBook()) );
    connect( editPhraseBookp, TQT_SIGNAL(activated(int)),
	     this, TQT_SLOT(editPhraseBook(int)) );
    connect( printPhraseBookp, TQT_SIGNAL(activated(int)),
	     this, TQT_SLOT(printPhraseBook(int)) );
    // File menu
    openAct = new Action( filep, tqtr("&Open..."), TQT_TQOBJECT(this), TQT_SLOT(open()),
			  "fileopen.png", TQAccel::stringToKey(tqtr("Ctrl+O")) );

    filep->insertSeparator();

    saveAct = new Action( filep, tqtr("&Save"), TQT_TQOBJECT(this), TQT_SLOT(save()),
			  "filesave.png", TQAccel::stringToKey(tqtr("Ctrl+S")) );
    saveAsAct = new Action( filep, tqtr("Save &As..."), TQT_TQOBJECT(this), TQT_SLOT(saveAs()) );
    releaseAct = new Action( filep, tqtr("&Release..."), TQT_TQOBJECT(this), TQT_SLOT(release()) );
    filep->insertSeparator();
    printAct = new Action( filep, tqtr("&Print..."), TQT_TQOBJECT(this), TQT_SLOT(print()),
			   "print.png", TQAccel::stringToKey(tqtr("Ctrl+P")) );

    filep->insertSeparator();

    recentFilesMenu = new TQPopupMenu( this );
    filep->insertItem( tqtr("Re&cently opened files"), recentFilesMenu );
    connect( recentFilesMenu, TQT_SIGNAL(aboutToShow()), this,
	     TQT_SLOT(setupRecentFilesMenu()) );
    connect( recentFilesMenu, TQT_SIGNAL(activated( int )), this,
	     TQT_SLOT(recentFileActivated( int )) );

    filep->insertSeparator();

    exitAct = new Action( filep, tqtr("E&xit"), TQT_TQOBJECT(this), TQT_SLOT(close()),
			  TQString(), TQAccel::stringToKey(tqtr("Ctrl+Q")) );
    // Edit menu
    undoAct = new Action( editp, tqtr("&Undo"), TQT_TQOBJECT(me), TQT_SLOT(undo()),
			  "undo.png", TQAccel::stringToKey(tqtr("Ctrl+Z")) );
    undoAct->setEnabled( FALSE );
    connect( me, TQT_SIGNAL(undoAvailable(bool)), undoAct, TQT_SLOT(setEnabled(bool)) );
    redoAct = new Action( editp, tqtr("&Redo"), TQT_TQOBJECT(me), TQT_SLOT(redo()),
			  "redo.png", TQAccel::stringToKey(tqtr("Ctrl+Y")) );
    redoAct->setEnabled( FALSE );
    connect( me, TQT_SIGNAL(redoAvailable(bool)), redoAct, TQT_SLOT(setEnabled(bool)) );
    editp->insertSeparator();
    cutAct = new Action( editp, tqtr("Cu&t"), TQT_TQOBJECT(me), TQT_SLOT(cut()),
			 "editcut.png", TQAccel::stringToKey(tqtr("Ctrl+X")) );
    cutAct->setEnabled( FALSE );
    connect( me, TQT_SIGNAL(cutAvailable(bool)), cutAct, TQT_SLOT(setEnabled(bool)) );
    copyAct = new Action( editp, tqtr("&Copy"), TQT_TQOBJECT(me), TQT_SLOT(copy()),
			  "editcopy.png", TQAccel::stringToKey(tqtr("Ctrl+C")) );
    copyAct->setEnabled( FALSE );
    connect( me, TQT_SIGNAL(copyAvailable(bool)), copyAct, TQT_SLOT(setEnabled(bool)) );
    pasteAct = new Action( editp, tqtr("&Paste"), TQT_TQOBJECT(me), TQT_SLOT(paste()),
			   "editpaste.png", TQAccel::stringToKey(tqtr("Ctrl+V")) );
    pasteAct->setEnabled( FALSE );
    connect( me, TQT_SIGNAL(pasteAvailable(bool)),
	     pasteAct, TQT_SLOT(setEnabled(bool)) );
    selectAllAct = new Action( editp, tqtr("Select &All"), TQT_TQOBJECT(me), TQT_SLOT(selectAll()),
			       TQString(), TQAccel::stringToKey(tqtr("Ctrl+A")) );
    selectAllAct->setEnabled( FALSE );
    editp->insertSeparator();
    findAct = new Action( editp, tqtr("&Find..."), TQT_TQOBJECT(this), TQT_SLOT(find()),
			  "searchfind.png", TQAccel::stringToKey(tqtr("Ctrl+F")) );
    findAct->setEnabled( FALSE );
    findAgainAct = new Action( editp, tqtr("Find &Next"),
			       TQT_TQOBJECT(this), TQT_SLOT(findAgain()), TQString(), Qt::Key_F3 );
    findAgainAct->setEnabled( FALSE );
#ifdef notyet
    replaceAct = new Action( editp, tqtr("&Replace..."), TQT_TQOBJECT(this), TQT_SLOT(replace()),
			     TQAccel::stringToKey(tqtr("Ctrl+H")) );
    replaceAct->setEnabled( FALSE );
#endif

    // Translation menu
    // when updating the accelerators, remember the status bar
    prevUnfinishedAct = new Action( translationp, tqtr("&Prev Unfinished"),
				    TQT_TQOBJECT(this), TQT_SLOT(prevUnfinished()),
				    "prevunfinished.png", TQAccel::stringToKey(tqtr("Ctrl+K")) );
    nextUnfinishedAct = new Action( translationp, tqtr("&Next Unfinished"),
				    TQT_TQOBJECT(this), TQT_SLOT(nextUnfinished()),
				    "nextunfinished.png", TQAccel::stringToKey(tqtr("Ctrl+L")) );

    prevAct = new Action( translationp, tqtr("P&rev"),
			  TQT_TQOBJECT(this), TQT_SLOT(prev()), "prev.png",
			  TQAccel::stringToKey(tqtr("Ctrl+Shift+K")) );
    nextAct = new Action( translationp, tqtr("Ne&xt"),
			  TQT_TQOBJECT(this), TQT_SLOT(next()), "next.png",
			  TQAccel::stringToKey(tqtr("Ctrl+Shift+L")) );
    doneAndNextAct = new Action( translationp, tqtr("Done and &Next"),
				 TQT_TQOBJECT(this), TQT_SLOT(doneAndNext()), "doneandnext.png",
				 TQAccel::stringToKey(tqtr("Ctrl+Enter")) );
    doneAndNextAlt = new TQAction( this );
    doneAndNextAlt->setAccel( TQAccel::stringToKey(tqtr("Ctrl+Return")) );
    connect( doneAndNextAlt, TQT_SIGNAL(activated()), this, TQT_SLOT(doneAndNext()) );
    beginFromSourceAct = new Action( translationp, tqtr("&Begin from Source"),
				     TQT_TQOBJECT(me), TQT_SLOT(beginFromSource()),
				     TQString(), TQAccel::stringToKey(tqtr("Ctrl+B")) );
    connect( me, TQT_SIGNAL(updateActions(bool)), beginFromSourceAct,
	     TQT_SLOT(setEnabled(bool)) );

    // Phrasebook menu
    newPhraseBookAct = new Action( phrasep, tqtr("&New Phrase Book..."),
				   TQT_TQOBJECT(this), TQT_SLOT(newPhraseBook()),
				   TQString(), TQAccel::stringToKey(tqtr("Ctrl+N")) );
    openPhraseBookAct = new Action( phrasep, tqtr("&Open Phrase Book..."),
				    TQT_TQOBJECT(this), TQT_SLOT(openPhraseBook()),
				    "book.png", TQAccel::stringToKey(tqtr("Ctrl+H")) );
    closePhraseBookId = phrasep->insertItem( tqtr("&Close Phrase Book"),
					     closePhraseBookp );
    phrasep->insertSeparator();
    editPhraseBookId = phrasep->insertItem( tqtr("&Edit Phrase Book..."),
					    editPhraseBookp );
    printPhraseBookId = phrasep->insertItem( tqtr("&Print Phrase Book..."),
					     printPhraseBookp );
    connect( phrasep, TQT_SIGNAL(aboutToShow()), this, TQT_SLOT(setupPhrase()) );

    // Validation menu
    acceleratorsAct = new Action( validationp, tqtr("&Accelerators"),
				  TQT_TQOBJECT(this), TQT_SLOT(revalidate()), "accelerator.png", 0, TRUE );
    acceleratorsAct->setOn( TRUE );
    endingPunctuationAct = new Action( validationp, tqtr("&Ending Punctuation"),
				       TQT_TQOBJECT(this), TQT_SLOT(revalidate()), "punctuation.png", 0, TRUE );
    endingPunctuationAct->setOn( TRUE );
    phraseMatchesAct = new Action( validationp, tqtr("&Phrase Matches"),
				   TQT_TQOBJECT(this), TQT_SLOT(revalidate()), "phrase.png", 0, TRUE );
    phraseMatchesAct->setOn( TRUE );

    // View menu
    revertSortingAct = new Action( viewp, tqtr("&Revert Sorting"),
				   TQT_TQOBJECT(this), TQT_SLOT(revertSorting()) );
    doGuessesAct = new Action( viewp, tqtr("&Display guesses"),
			       TQT_TQOBJECT(this), TQT_SLOT(toggleGuessing()) );
    doGuessesAct->setToggleAction( TRUE );
    doGuessesAct->setOn( TRUE );
    toggleStats = new Action( viewp, tqtr("&Statistics"), TQT_TQOBJECT(this), TQT_SLOT(toggleStatistics()) );
    toggleStats->setToggleAction( TRUE );
    viewp->insertSeparator();
    viewp->insertItem( tqtr("Vie&ws"), createDockWindowMenu( NoToolBars ) );
    viewp->insertItem( tqtr("&Toolbars"), createDockWindowMenu( OnlyToolBars ) );

    // Help
    manualAct = new Action( helpp, tqtr("&Manual"), TQT_TQOBJECT(this), TQT_SLOT(manual()), 0,
			    Qt::Key_F1 );
    helpp->insertSeparator();
    aboutAct = new Action( helpp, tqtr("&About"), TQT_TQOBJECT(this), TQT_SLOT(about()) );
    aboutTQtAct = new Action( helpp, tqtr("About &TQt"), TQT_TQOBJECT(this), TQT_SLOT(aboutTQt()) );
    helpp->insertSeparator();
    whatsThisAct = new Action( helpp, tqtr("&What's This?"),
			       TQT_TQOBJECT(this), TQT_SLOT(whatsThis()), "whatsthis.xpm", Qt::SHIFT + Qt::Key_F1 );

    openAct->setWhatsThis( tqtr("Open a TQt translation source file (TS file) for"
			      " editing.") );
    saveAct->setWhatsThis( tqtr("Save changes made to this TQt translation "
				"source file.") );
    saveAsAct->setWhatsThis( tqtr("Save changes made to this TQt translation"
				"source file into a new file.") );
    releaseAct->setWhatsThis( tqtr("Create a TQt message file suitable for"
				 " released applications"
				 " from the current message file.") );
    printAct->setWhatsThis( tqtr("Print a list of all the phrases in the current"
			       " TQt translation source file.") );
    exitAct->setWhatsThis( tqtr("Close this window and exit.") );

    undoAct->setWhatsThis( tqtr("Undo the last editing operation performed on the"
			      " translation.") );
    redoAct->setWhatsThis( tqtr("Redo an undone editing operation performed on"
			      " the translation.") );
    cutAct->setWhatsThis( tqtr("Copy the selected translation text to the"
			     " clipboard and deletes it.") );
    copyAct->setWhatsThis( tqtr("Copy the selected translation text to the"
			      " clipboard.") );
    pasteAct->setWhatsThis( tqtr("Paste the clipboard text into the"
			       " translation.") );
    selectAllAct->setWhatsThis( tqtr("Select the whole translation text.") );
    findAct->setWhatsThis( tqtr("Search for some text in the translation "
				"source file.") );
    findAgainAct->setWhatsThis( tqtr("Continue the search where it was left.") );
#ifdef notyet
    replaceAct->setWhatsThis( tqtr("Search for some text in the translation"
				 " source file and replace it by another"
				 " text.") );
#endif

    newPhraseBookAct->setWhatsThis( tqtr("Create a new phrase book.") );
    openPhraseBookAct->setWhatsThis( tqtr("Open a phrase book to assist"
					" translation.") );
    acceleratorsAct->setWhatsThis( tqtr("Toggle validity checks of"
				      " accelerators.") );
    endingPunctuationAct->setWhatsThis( tqtr("Toggle validity checks"
					   " of ending punctuation.") );
    phraseMatchesAct->setWhatsThis( tqtr("Toggle checking that phrase"
				       " suggestions are used.") );

    revertSortingAct->setWhatsThis( tqtr("Sort the items back in the same order"
				       " as in the message file.") );

    doGuessesAct->setWhatsThis( tqtr("Set whether or not to display translation guesses.") );
    manualAct->setWhatsThis( tqtr("Display the manual for %1.")
			       .arg(tqtr("TQt Linguist")) );
    aboutAct->setWhatsThis( tqtr("Display information about %1.")
			    .arg(tqtr("TQt Linguist")) );
    aboutTQtAct->setWhatsThis( tqtr("Display information about the TQt toolkit by"
				 " Trolltech.") );
    whatsThisAct->setWhatsThis( tqtr("Enter What's This? mode.") );

    beginFromSourceAct->setWhatsThis( tqtr("Copies the source text into"
					 " the translation field.") );
    nextAct->setWhatsThis( tqtr("Moves to the next item.") );
    prevAct->setWhatsThis( tqtr("Moves to the previous item.") );
    nextUnfinishedAct->setWhatsThis( tqtr("Moves to the next unfinished item.") );
    prevUnfinishedAct->setWhatsThis( tqtr("Moves to the previous unfinished item.") );
    doneAndNextAct->setWhatsThis( tqtr("Marks this item as done and moves to the"
				     " next unfinished item.") );
    doneAndNextAlt->setWhatsThis( doneAndNextAct->whatsThis() );
}

void TrWindow::setupToolBars()
{
    TQToolBar *filet = new TQToolBar( tqtr("File"), this );
    TQToolBar *editt = new TQToolBar( tqtr("Edit"), this );
    TQToolBar *translationst = new TQToolBar( tqtr("Translation"), this );
    TQToolBar *validationt   = new TQToolBar( tqtr("Validation"), this );
    TQToolBar *helpt = new TQToolBar( tqtr("Help"), this );

    openAct->addToToolbar( filet, tqtr("Open") );
    saveAct->addToToolbar( filet, tqtr("Save") );
    printAct->addToToolbar( filet, tqtr("Print") );
    filet->addSeparator();
    openPhraseBookAct->addToToolbar( filet, tqtr("Open Phrase Book") );

    undoAct->addToToolbar( editt, tqtr("Undo") );
    redoAct->addToToolbar( editt, tqtr("Redo") );
    editt->addSeparator();
    cutAct->addToToolbar( editt, tqtr("Cut") );
    copyAct->addToToolbar( editt, tqtr("Copy") );
    pasteAct->addToToolbar( editt, tqtr("Paste") );
    editt->addSeparator();
    findAct->addToToolbar( editt, tqtr("Find") );
#ifdef notyet
    replaceAct->addToToolbar( editt, tqtr("Replace") );
#endif

    // beginFromSourceAct->addToToolbar( translationst,
    //                                tqtr("Begin from Source"), "searchfind" );
    prevAct->addToToolbar( translationst, tqtr("Prev") );
    nextAct->addToToolbar( translationst, tqtr("Next") );
    prevUnfinishedAct->addToToolbar( translationst, tqtr("Prev Unfinished") );
    nextUnfinishedAct->addToToolbar( translationst, tqtr("Next Unfinished") );
    doneAndNextAct->addToToolbar( translationst, tqtr("Done and Next") );

    acceleratorsAct->addToToolbar( validationt, tqtr("Accelerators") );
    endingPunctuationAct->addToToolbar( validationt, tqtr("Punctuation") );
    phraseMatchesAct->addToToolbar( validationt, tqtr("Phrases") );

    whatsThisAct->addToToolbar( helpt, tqtr("What's This?") );
}

void TrWindow::setCurrentContextItem( TQListViewItem *item )
{
    lv->ensureItemVisible( item );
    lv->setSelected( item, TRUE );
}

void TrWindow::setCurrentMessageItem( TQListViewItem *item )
{
    slv->ensureItemVisible( item );
    slv->setSelected( item, TRUE );
}

TQString TrWindow::friendlyPhraseBookName( int k )
{
    return TQFileInfo( phraseBookNames[k] ).fileName();
}

bool TrWindow::openPhraseBook( const TQString& name )
{
    PhraseBook *pb = new PhraseBook;
    if ( !pb->load(name) ) {
	TQMessageBox::warning( this, tqtr("TQt Linguist"),
			      tqtr("Cannot read from phrase book '%1'.")
			      .arg(name) );
	return FALSE;
    }

    int index = (int) phraseBooks.count();
    phraseBooks.append( pb );
    phraseBookNames.append( name );
    int id = closePhraseBookp->insertItem( friendlyPhraseBookName(index) );
    closePhraseBookp->TQMenuData::setWhatsThis( id, tqtr("Close this phrase book.") );
    id = editPhraseBookp->insertItem( friendlyPhraseBookName(index) );
    editPhraseBookp->TQMenuData::setWhatsThis( id, tqtr("Allow you to add, modify, or delete"
					  " phrases of this phrase book.") );
    id = printPhraseBookp->insertItem( friendlyPhraseBookName(index) );
    printPhraseBookp->TQMenuData::setWhatsThis( id, tqtr("Print the entries of the phrase"
					   " book.") );
    updatePhraseDict();
    return TRUE;
}

bool TrWindow::savePhraseBook( TQString& name, const PhraseBook& pb )
{
    if ( !name.contains( ".qph" ) && !name.contains(".") )
	name += ".qph";

    if ( !pb.save(name) ) {
	TQMessageBox::warning( this, tqtr("TQt Linguist"),
			      tqtr("Cannot create phrase book '%1'.")
			      .arg(name) );
	return FALSE;
    }
    return TRUE;
}

void TrWindow::updateProgress()
{
    if ( numNonobsolete == 0 )
	progress->setText( TQString("    " "    ") );
    else
	progress->setText( TQString(" %1/%2 ").arg(numFinished)
			   .arg(numNonobsolete) );
    prevUnfinishedAct->setEnabled( numFinished != numNonobsolete );
    nextUnfinishedAct->setEnabled( numFinished != numNonobsolete );
    prevAct->setEnabled( lv->firstChild() != 0 );
    nextAct->setEnabled( lv->firstChild() != 0 );
}

void TrWindow::updatePhraseDict()
{
    TQPtrListIterator<PhraseBook> pb = phraseBooks;
    PhraseBook::Iterator p;
    PhraseBook *ent;
    phraseDict.clear();
    while ( pb.current() != 0 ) {
	for ( p = (*pb)->begin(); p != (*pb)->end(); ++p ) {
	    TQString f = friendlyString( (*p).source() );
	    if ( f.length() > 0 ) {
		f = TQStringList::split( TQChar(' '), f ).first();
		ent = phraseDict.find( f );
		if ( ent == 0 ) {
		    ent = new PhraseBook;
		    phraseDict.insert( f, ent );
		}
		ent->append( *p );
	    }
	}
	++pb;
    }
    revalidate();
}

PhraseBook TrWindow::getPhrases( const TQString& source )
{
    PhraseBook phrases;
    TQString f = friendlyString( source );
    TQStringList lookupWords = TQStringList::split( TQChar(' '), f );
    TQStringList::Iterator w;
    PhraseBook::Iterator p;

    for ( w = lookupWords.begin(); w != lookupWords.end(); ++w ) {
	PhraseBook *ent = phraseDict.find( *w );
	if ( ent != 0 ) {
	    for ( p = ent->begin(); p != ent->end(); ++p ) {
		if ( f.find(friendlyString((*p).source())) >= 0 )
		    phrases.append( *p );
	    }
	}
    }
    return phrases;
}

bool TrWindow::danger( const TQString& source, const TQString& translation,
		       bool verbose )
{
    if ( acceleratorsAct->isOn() ) {
	int sk = TQAccel::shortcutKey( source );
	int tk = TQAccel::shortcutKey( translation );
	if ( sk == 0 && tk != 0 ) {
	    if ( verbose )
		statusBar()->message( tqtr("Accelerator possibly superfluous in"
					 " translation."), ErrorMS );
	    return TRUE;
	} else if ( sk != 0 && tk == 0 ) {
	    if ( verbose )
		statusBar()->message( tqtr("Accelerator possibly missing in"
					 " translation."), ErrorMS );
	    return TRUE;
	}
    }
    if ( endingPunctuationAct->isOn() ) {
	if ( ending(source) != ending(translation) ) {
	    if ( verbose )
		statusBar()->message( tqtr("Translation does not end with the"
					 " same punctuation as the source"
					 " text."), ErrorMS );
	    return TRUE;
	}
    }
    if ( phraseMatchesAct->isOn() ) {
	TQString fsource = friendlyString( source );
	TQString ftranslation = friendlyString( translation );
	TQStringList lookupWords = TQStringList::split( TQChar(' '), fsource );
	TQStringList::Iterator w;
	PhraseBook::Iterator p;

	for ( w = lookupWords.begin(); w != lookupWords.end(); ++w ) {
	    PhraseBook *ent = phraseDict.find( *w );
	    if ( ent != 0 ) {
		for ( p = ent->begin(); p != ent->end(); ++p ) {
		    if ( fsource.find(friendlyString((*p).source())) < 0 ||
			 ftranslation.find(friendlyString((*p).target())) >= 0 )
			break;
		}
		if ( p == ent->end() ) {
		    if ( verbose )
			statusBar()->message( tqtr("A phrase book suggestion for"
						 " '%1' was ignored.")
						 .arg(*w), ErrorMS );
		    return TRUE;
		}
	    }
	}
    }
    if ( verbose )
	statusBar()->clear();

    return FALSE;
}

void TrWindow::readConfig()
{
    TQString keybase( "/TQt Linguist/" +
		     TQString::number( (TQT_VERSION >> 16) & 0xff ) +
		     "." + TQString::number( (TQT_VERSION >> 8) & 0xff ) + "/" );
    TQSettings config;

    config.insertSearchPath( TQSettings::Windows, "/Trolltech" );

    TQRect r( pos(), size() );
    recentFiles = config.readListEntry( keybase + "RecentlyOpenedFiles" );
    if ( !config.readBoolEntry( keybase + "Geometry/MainwindowMaximized", FALSE ) ) {
	r.setX( config.readNumEntry( keybase + "Geometry/MainwindowX", r.x() ) );
	r.setY( config.readNumEntry( keybase + "Geometry/MainwindowY", r.y() ) );
	r.setWidth( config.readNumEntry( keybase + "Geometry/MainwindowWidth", r.width() ) );
	r.setHeight( config.readNumEntry( keybase + "Geometry/MainwindowHeight", r.height() ) );

	TQRect desk = TQApplication::desktop()->tqgeometry();
	TQRect inter = desk.intersect( r );
	resize( r.size() );
	if ( inter.width() * inter.height() > ( r.width() * r.height() / 20 ) ) {
	    move( r.topLeft() );
	}
    }

    TQDockWindow * dw;
    dw = (TQDockWindow *) lv->parent();
    int place;
    place = config.readNumEntry( keybase + "Geometry/ContextwindowInTQt::Dock" );
    r.setX( config.readNumEntry( keybase + "Geometry/ContextwindowX" ) );
    r.setY( config.readNumEntry( keybase + "Geometry/ContextwindowY" ) );
    r.setWidth( config.readNumEntry( keybase +
				     "Geometry/ContextwindowWidth" ) );
    r.setHeight( config.readNumEntry( keybase +
				      "Geometry/ContextwindowHeight" ) );
    if ( place == TQDockWindow::OutsideDock ) {
	dw->undock();
	dw->show();
    }
    dw->setGeometry( r );

    dw = (TQDockWindow *) slv->parent();
    place = config.readNumEntry( keybase + "Geometry/SourcewindowInTQt::Dock" );
    r.setX( config.readNumEntry( keybase + "Geometry/SourcewindowX" ) );
    r.setY( config.readNumEntry( keybase + "Geometry/SourcewindowY" ) );
    r.setWidth( config.readNumEntry( keybase +
				     "Geometry/SourcewindowWidth" ) );
    r.setHeight( config.readNumEntry( keybase +
				      "Geometry/SourcewindowHeight" ) );
    if ( place == TQDockWindow::OutsideDock ) {
	dw->undock();
	dw->show();
    }
    dw->setGeometry( r );

    dw = (TQDockWindow *) plv->parent()->parent();
    place = config.readNumEntry( keybase + "Geometry/PhrasewindowInTQt::Dock" );
    r.setX( config.readNumEntry( keybase + "Geometry/PhrasewindowX" ) );
    r.setY( config.readNumEntry( keybase + "Geometry/PhrasewindowY" ) );
    r.setWidth( config.readNumEntry( keybase +
				     "Geometry/PhrasewindowWidth" ) );
    r.setHeight( config.readNumEntry( keybase +
				      "Geometry/PhrasewindowHeight" ) );
    if ( place == TQDockWindow::OutsideDock ) {
	dw->undock();
	dw->show();
    }
    dw->setGeometry( r );
    TQApplication::sendPostedEvents();
}

void TrWindow::writeConfig()
{
    TQString keybase( "/TQt Linguist/" +
		     TQString::number( (TQT_VERSION >> 16) & 0xff ) +
		     "." + TQString::number( (TQT_VERSION >> 8) & 0xff ) + "/" );
    TQSettings config;

    config.insertSearchPath( TQSettings::Windows, "/Trolltech" );
    config.writeEntry( keybase + "RecentlyOpenedFiles", recentFiles );
    config.writeEntry( keybase + "Geometry/MainwindowMaximized", isMaximized() );
    config.writeEntry( keybase + "Geometry/MainwindowX", x() );
    config.writeEntry( keybase + "Geometry/MainwindowY", y() );
    config.writeEntry( keybase + "Geometry/MainwindowWidth", width() );
    config.writeEntry( keybase + "Geometry/MainwindowHeight", height() );

    TQDockWindow * dw =(TQDockWindow *) lv->parent();
    config.writeEntry( keybase + "Geometry/ContextwindowInTQt::Dock", dw->place() );
    config.writeEntry( keybase + "Geometry/ContextwindowX", dw->x() );
    config.writeEntry( keybase + "Geometry/ContextwindowY", dw->y() );
    config.writeEntry( keybase + "Geometry/ContextwindowWidth", dw->width() );
    config.writeEntry( keybase + "Geometry/ContextwindowHeight", dw->height() );

    dw =(TQDockWindow *) slv->parent();
    config.writeEntry( keybase + "Geometry/SourcewindowInTQt::Dock",
		       dw->place() );
    config.writeEntry( keybase + "Geometry/SourcewindowX", dw->tqgeometry().x() );
    config.writeEntry( keybase + "Geometry/SourcewindowY", dw->tqgeometry().y() );
    config.writeEntry( keybase + "Geometry/SourcewindowWidth", dw->width() );
    config.writeEntry( keybase + "Geometry/SourcewindowHeight", dw->height() );

    dw =(TQDockWindow *) plv->parent()->parent();
    config.writeEntry( keybase + "Geometry/PhrasewindowInTQt::Dock",
		       dw->place() );
    config.writeEntry( keybase + "Geometry/PhrasewindowX", dw->tqgeometry().x() );
    config.writeEntry( keybase + "Geometry/PhrasewindowY", dw->tqgeometry().y() );
    config.writeEntry( keybase + "Geometry/PhrasewindowWidth", dw->width() );
    config.writeEntry( keybase + "Geometry/PhrasewindowHeight", dw->height() );
}

void TrWindow::setupRecentFilesMenu()
{
    recentFilesMenu->clear();
    int id = 0;
    TQStringList::Iterator it = recentFiles.begin();
    for ( ; it != recentFiles.end(); ++it )
    {
	recentFilesMenu->insertItem( *it, id );
	id++;
    }
}

void TrWindow::recentFileActivated( int id )
{
    if ( id != -1 ) {
	if ( maybeSave() )
	    openFile( *recentFiles.at( id ) );
    }
}

void TrWindow::addRecentlyOpenedFile( const TQString &fn, TQStringList &lst )
{
    if ( lst.find( fn ) != lst.end() )
	return;
    if ( lst.count() >= 10 )
	lst.remove( lst.begin() );
    lst << fn;
}

void TrWindow::toggleGuessing()
{
    me->toggleGuessing();
}

void TrWindow::focusSourceList()
{
    slv->setFocus();
}

void TrWindow::focusPhraseList()
{
    plv->setFocus();
}

void TrWindow::updateClosePhraseBook()
{
    if ( dirtyItem != -1 ) {
	closePhraseBookp->removeItem( closePhraseBookp->idAt(dirtyItem) );
	dirtyItem = -1;
    }
}

void TrWindow::toggleStatistics()
{
    if ( toggleStats->isOn() ) {
	if ( !stats ) {
	    stats = new Statistics( this, "linguist_stats" );
	    connect( this, TQT_SIGNAL(statsChanged(int,int,int,int,int,int)), stats,
		     TQT_SLOT(updateStats(int,int,int,int,int,int)) );
	    connect( stats, TQT_SIGNAL(closed()), toggleStats, TQT_SLOT(toggle()) );
	}
	updateStatistics();
	stats->show();
    } else if ( stats ) {
	stats->close();
    }
}

void TrWindow::updateStatistics()
{
    TQListViewItem * ci = lv->firstChild();
    int trW = 0;
    int trC = 0;
    int trCS = 0;
    while ( ci ) {
 	countStats( ci, ((ContextLVI *)ci)->firstMessageItem(), trW, trC, trCS );
	ci = ci->nextSibling();
    }
    // ..and the items in the source list
    countStats( 0, slv->firstChild(), trW, trC, trCS );
    emit statsChanged( srcWords, srcChars, srcCharsSpc, trW, trC, trCS );
}


void TrWindow::countStats( TQListViewItem* ci, TQListViewItem* mi, int& trW, int& trC, int& trCS )
{
    MessageLVI * m;
    while ( mi ) {
	m = (MessageLVI *) mi;
	if ( m->finished() && !(m->message().type() == MetaTranslatorMessage::Obsolete) )
	    doCharCounting( m->translation(), trW, trC, trCS );
	if ( ci )
	    mi = ((ContextLVI *)ci)->nextMessageItem();
	else
	    mi = mi->nextSibling();
    }
}

void TrWindow::doCharCounting( const TQString& text, int& trW, int& trC, int& trCS )
{
    trCS += text.length();
    bool inWord = FALSE;
    for ( int i = 0; i < (int) text.length(); i++ ) {
	if ( text[i].isLetterOrNumber() || text[i] == TQChar('_') ) {
	    if ( !inWord ) {
		trW++;
		inWord = TRUE;
	    }
	} else {
	    inWord = FALSE;
	}
	if ( !text[i].isSpace() )
	    trC++;
    }
}
