/**
 This file is part of Kiten, a KDE Japanese Reference Tool...
 Copyright (C) 2001  Jason Katz-Brown <jason@katzbrown.com>
	       (C) 2005 Paul Temple <paul.temple@gmx.net>

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

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

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
 USA
**/

#include <tdeaction.h>
#include <tdeapplication.h>
#include <tdeconfig.h>
#include <tdeconfigdialog.h>
#include <kdebug.h>
#include <tdeversion.h>
#include <kedittoolbar.h>
#include <kglobalaccel.h>
#include <kiconloader.h>
#include <kkeydialog.h>
#include <tdelocale.h>
#include <tdemainwindow.h>
#include <kstandarddirs.h>
#include <kstatusbar.h>

#include <tqclipboard.h>
#include <tqregexp.h>
#include <tqtextcodec.h>
#include <tqtimer.h>

#include "kiten.h"
#include "learn.h"
#include "kitenconfig.h"
#include "optiondialog.h"

#include <cassert>

TopLevel::TopLevel(TQWidget *parent, const char *name) : TDEMainWindow(parent, name)
{
#if TDE_VERSION > 305
	    setStandardToolBarMenuEnabled(true);
#endif

	config = Config::self();
	config->readConfig();
	Accel = new TDEGlobalAccel(TQT_TQOBJECT(this));
	(void) Accel->insert("Lookup Kanji (Kanjidic)", i18n("Lookup Kanji (Kanjidic)"), i18n("Gives detailed information about Kanji currently on clipboard."), CTRL + ALT + Key_K, CTRL + ALT + Key_K, TQT_TQOBJECT(this), TQT_SLOT(kanjiSearchAccel()));
	(void) Accel->insert("Lookup English/Japanese word", i18n("Lookup English/Japanese Word"), i18n("Looks up current text on clipboard in the same way as if you used Kiten's regular search."), CTRL + ALT + Key_S, CTRL + ALT + Key_S, TQT_TQOBJECT(this), TQT_SLOT(searchAccel()));
	Accel->readSettings(TDEGlobal::config());
	Accel->updateConnections();

	_ResultView = new ResultView(true, this, "_ResultView");
	setCentralWidget(_ResultView);

	(void) KStdAction::quit(TQT_TQOBJECT(this), TQT_SLOT(close()), actionCollection());
	(void) KStdAction::print(TQT_TQOBJECT(this), TQT_SLOT(print()), actionCollection());
	(void) KStdAction::preferences(TQT_TQOBJECT(this), TQT_SLOT(slotConfigure()), actionCollection());
	KStdAction::keyBindings(guiFactory(), TQT_SLOT(configureShortcuts()), actionCollection());
	(void) new TDEAction(i18n("&Learn"), "pencil", CTRL+Key_L, TQT_TQOBJECT(this), TQT_SLOT(createLearn()), actionCollection(), "file_learn");
	(void) new TDEAction(i18n("&Dictionary Editor..."), "edit", 0, TQT_TQOBJECT(this), TQT_SLOT(createEEdit()), actionCollection(), "dict_editor");
	(void) new TDEAction(i18n("Ra&dical Search..."), "gear", CTRL+Key_R, TQT_TQOBJECT(this), TQT_SLOT(radicalSearch()), actionCollection(), "search_radical");
	Edit = new EditAction(i18n("Search Edit"), 0, TQT_TQOBJECT(this), TQT_SLOT(search()), actionCollection(), "search_edit");
	(void) new TDEAction(i18n("&Clear Search Bar"), BarIcon("locationbar_erase", 16), CTRL+Key_N, Edit, TQT_SLOT(clear()), actionCollection(), "clear_search");
	(void) new TDEAction(i18n("S&earch"), "key_enter", 0, TQT_TQOBJECT(this), TQT_SLOT(search()), actionCollection(), "search");
	(void) new TDEAction(i18n("Search with &Beginning of Word"), 0, TQT_TQOBJECT(this), TQT_SLOT(searchBeginning()), actionCollection(), "search_beginning");
	(void) new TDEAction(i18n("Search &Anywhere"), 0, TQT_TQOBJECT(this), TQT_SLOT(searchAnywhere()), actionCollection(), "search_anywhere");
	(void) new TDEAction(i18n("Stro&kes"), "paintbrush", CTRL+Key_S, TQT_TQOBJECT(this), TQT_SLOT(strokeSearch()), actionCollection(), "search_stroke");
	(void) new TDEAction(i18n("&Grade"), "format-justify-left", CTRL+Key_G, TQT_TQOBJECT(this), TQT_SLOT(gradeSearch()), actionCollection(), "search_grade");
	kanjiCB = new TDEToggleAction(i18n("&Kanjidic"), "kanjidic", CTRL+Key_K, TQT_TQOBJECT(this), TQT_SLOT(kanjiDictChange()), actionCollection(), "kanji_toggle");
	deinfCB = new TDEToggleAction(i18n("&Deinflect Verbs in Regular Search"), 0, TQT_TQOBJECT(this), TQT_SLOT(kanjiDictChange()), actionCollection(), "deinf_toggle");
	comCB = new TDEToggleAction(i18n("&Filter Rare"), "filter", CTRL+Key_F, TQT_TQOBJECT(this), TQT_SLOT(toggleCom()), actionCollection(), "common");
	autoSearchToggle = new TDEToggleAction(i18n("&Automatically Search Clipboard Selections"), "edit-find", 0, TQT_TQOBJECT(this), TQT_SLOT(kanjiDictChange()), actionCollection(), "autosearch_toggle");
	irAction =  new TDEAction(i18n("Search &in Results"), "edit-find", CTRL+Key_I, TQT_TQOBJECT(this), TQT_SLOT(resultSearch()), actionCollection(), "search_in_results");
	(void) KStdAction::configureToolbars(TQT_TQOBJECT(this), TQT_SLOT(configureToolBars()), actionCollection());
	addAction = new TDEAction(i18n("Add &Kanji to Learning List"), 0, TQT_TQOBJECT(this), TQT_SLOT(addToList()), actionCollection(), "add");
	addAction->setEnabled(false);
	(void) new TDEAction(i18n("Configure &Global Shortcuts..."), "configure_shortcuts", 0, TQT_TQOBJECT(this), TQT_SLOT(configureGlobalKeys()), actionCollection(), "options_configure_keybinding");

	historyAction = new TDEListAction(i18n("&History"), 0, 0, 0, actionCollection(), "history");
	connect(historyAction, TQT_SIGNAL(activated(int)), TQT_TQOBJECT(this), TQT_SLOT(goInHistory(int)));
	backAction = KStdAction::back(TQT_TQOBJECT(this), TQT_SLOT(back()), actionCollection());
	forwardAction = KStdAction::forward(TQT_TQOBJECT(this), TQT_SLOT(forward()), actionCollection());
	backAction->setEnabled(false);
	forwardAction->setEnabled(false);
	currentResult = resultHistory.end();
	currentResultIndex = 0;

	createGUI();

	StatusBar = statusBar();
	optionDialog = 0;

	comCB->setChecked(config->com());
	kanjiCB->setChecked(config->kanji());
	autoSearchToggle->setChecked(config->autosearch());
	deinfCB->setChecked(config->deinf());

	updateConfiguration();

	if (config->startLearn())
		createLearn();

	resize(600, 400);
	applyMainWindowSettings(TDEGlobal::config(), "TopLevelWindow");

	connect(_ResultView, TQT_SIGNAL(linkClicked(const TQString &)), TQT_SLOT(ressearch(const TQString &)));
	connect(kapp->clipboard(), TQT_SIGNAL(selectionChanged()), TQT_TQOBJECT(this), TQT_SLOT(autoSearch()));

	TQTimer::singleShot(10, TQT_TQOBJECT(this), TQT_SLOT(finishInit()));
}

TopLevel::~TopLevel()
{
    delete optionDialog;
    optionDialog = 0;
}

void TopLevel::finishInit()
{
	// if it's the application's first time starting, 
	// the app group won't exist and we show demo
	if (!kapp->config()->hasGroup("app"))
	{
		if (kanjiCB->isChecked())
		  	Edit->setText(TQString::fromUtf8("辞"));
		else
		  	Edit->setText(TQString::fromUtf8("辞書"));

		search();
	}

	Edit->clear(); // make sure the edit is focused initially
	StatusBar->message(i18n("Welcome to Kiten"));
	setCaption(TQString());
}

bool TopLevel::queryClose()
{
	for (TQPtrListIterator<Learn> i(learnList); *i;)
	{
		(*i)->show();
		if (!(*i)->closeWindow())
			return false; // cancel
		Learn *old = *i;
		++i;
		learnList.remove(old);
	}

	config->setCom(comCB->isChecked());
	config->setKanji(kanjiCB->isChecked());
	config->setAutosearch(autoSearchToggle->isChecked());
	config->setDeinf(deinfCB->isChecked());
	config->writeConfig();

	saveMainWindowSettings(TDEGlobal::config(), "TopLevelWindow");
	return true;
}

void TopLevel::addToList()
{
	if (learnList.isEmpty())
		createLearn();
	else
		StatusBar->message(i18n("%1 added to learn list of all open learn windows").arg(toAddKanji.kanji()));

	emit add(toAddKanji);
}

void TopLevel::doSearch(const TQString &text, TQRegExp regexp)
{
	if (text.isEmpty())
	{
		StatusBar->message(i18n("Empty search items"));
		return;
	}

	StatusBar->message(i18n("Searching..."));

	Dict::SearchResult results;
	if (kanjiCB->isChecked())
	{
		results = _Asyndeta.retrieveIndex()->searchKanji(regexp, text, comCB->isChecked());
	}
	else
	{
		results = _Asyndeta.retrieveIndex()->search(regexp, text, comCB->isChecked());

		// do again... bad because sometimes reading is kanji
		if ((readingSearch || beginningReadingSearch) && (results.count < 1))
		{
			//kdDebug() << "doing again\n";

			if (beginningReadingSearch)
				regexp = kanjiSearchItems(true);
			else if (readingSearch)
				regexp = kanjiSearchItems();

			results = _Asyndeta.retrieveIndex()->search(regexp, text, comCB->isChecked());
		}
	}

	addHistory(results);
	handleSearchResult(results);
	readingSearch = false;
}

void TopLevel::doSearchInResults(const TQString &text, TQRegExp regexp)
{
	if (text.isEmpty())
	{
		StatusBar->message(i18n("Empty search items"));
		return;
	}

	StatusBar->message(i18n("Searching..."));
	Dict::SearchResult results = _Asyndeta.retrieveIndex()->searchPrevious(regexp, text, *currentResult, comCB->isChecked());
	addHistory(results);
	handleSearchResult(results);
	readingSearch = false;
}

void TopLevel::handleSearchResult(Dict::SearchResult results)
{
	Edit->setText(results.text);
	setResults(results.count, results.outOf);

	addAction->setEnabled(false);
	_ResultView->clear();

	Dict::Entry first = Dict::firstEntry(results);

	if (results.count > 0)
		kanjiCB->setChecked(first.extendedKanjiInfo());
	else
	{
		_ResultView->flush();
		return;
	}

	if (first.extendedKanjiInfo())
	{
		if (results.count == 1) // if its only one entry, give compounds too!
		{
			toAddKanji = first;
			_ResultView->addKanjiResult(toAddKanji, results.common, _Rad.radByKanji(toAddKanji.kanji()));

			addAction->setEnabled(true);

			_ResultView->append(TQString("<p>%1</p>").arg(i18n("HTML Entity: %1").arg(TQString("&amp;#x%1;").arg(TQString::number(toAddKanji.kanji().at(0).unicode(), 16))))); // show html entity

			// now show some compounds in which this kanji appears
			TQString kanji = toAddKanji.kanji();

			_ResultView->addHeader(i18n("%1 in compounds").arg(kanji));

			Dict::SearchResult compounds = _Asyndeta.retrieveIndex()->search(TQRegExp(kanji), kanji, true);
			bool common = true;
			if (compounds.count <= 0)
			{
				compounds = _Asyndeta.retrieveIndex()->search(TQRegExp(kanji), kanji, false);
				_ResultView->addHeader(i18n("(No common compounds)"), 4);
				common = false;
			}

			for (TQValueListIterator<Dict::Entry> it = compounds.list.begin(); it != compounds.list.end(); ++it)
			{
				//kdDebug() << "adding " << (*it).kanji() << endl;
				_ResultView->addResult(*it, common);
				kapp->processEvents();
			}
		}
		else
		{
			for (TQValueListIterator<Dict::Entry> it = results.list.begin(); it != results.list.end(); ++it)
			{
				kapp->processEvents();
				_ResultView->addKanjiResult(*it, results.common);
			}
		}
	}
	else
	{
		for (TQValueListIterator<Dict::Entry> it = results.list.begin(); it != results.list.end(); ++it)
		{
			kapp->processEvents();
			_ResultView->addResult(*it, comCB->isChecked());
		}
	}

	_ResultView->flush();
}

void TopLevel::searchBeginning()
{
	TQString text = Edit->text();
	TQRegExp regexp;

	switch (Dict::textType(text))
	{
	case Dict::Text_Latin:
		regexp = TQRegExp(TQString("\\W").append(text));
		break;

	case Dict::Text_Kana:
		if (kanjiCB->isChecked())
		{
			regexp = TQRegExp(TQString("\\W").append(text));
		}
		else
		{
			beginningReadingSearch = true;
			regexp = TQRegExp(TQString("\\[").append(text));
		}
		break;

	case Dict::Text_Kanji:
		regexp = TQRegExp(TQString("^").append(text));
	}

	doSearch(text, regexp);
}

void TopLevel::searchAnywhere()
{
	doSearch(Edit->text(), TQRegExp(Edit->text()));
}

void TopLevel::resultSearch()
{
	search(true);
}

// called when a kanji is clicked on in result view
void TopLevel::ressearch(const TQString &text)
{
	//kdDebug() << "ressearch(" << text << endl;
	if (text.startsWith("__radical:"))
	{
		TQString radical = text.section(":", 1, 1).right(1);
		//kdDebug() << "radical is " << radical << endl;
		radicalSearch()->addRadical(radical);
		return;
	}
	Edit->setText(text);
	kanjiCB->setChecked(true);
	search();
}

void TopLevel::search(bool inResults)
{
	TQString text = Edit->text();
	TQRegExp regexp;

	switch (Dict::textType(text))
	{
	case Dict::Text_Latin:
		regexp = searchItems();
		break;

	case Dict::Text_Kana:
		regexp = readingSearchItems(kanjiCB->isChecked());
		readingSearch = true;
		break;

	case Dict::Text_Kanji:
		if (deinfCB->isChecked()) // deinflect
		{
			bool common = comCB->isChecked();
			TQStringList names;
			TQStringList res(_DeinfIndex.deinflect(text, names));

			if (res.size() > 0)
			{
				Dict::SearchResult hist;
				hist.count = 0;
				hist.outOf = 0;
				hist.common = common;
				hist.text = text;

				TQStringList done;

				res.prepend(text);
				names.prepend(i18n("No deinflection"));

				TQStringList::Iterator nit = names.begin();
				for (TQStringList::Iterator it = res.begin(); it != res.end(); ++it, ++nit)
				{
					if (done.contains(*it) > 0)
						continue;

					//kdDebug() << "currently on deinflection " << *it << endl;
					Dict::SearchResult results = _Asyndeta.retrieveIndex()->search(TQRegExp(TQString("^") + (*it) + "\\W"), *it, common);

					if (results.count < 1) // stop if it isn't in the dictionary
						continue;

					hist.list.append(Dict::Entry(*nit, true));

					hist.list += results.list;
					hist.results += results.results;

					hist.count += results.count;
					hist.outOf += results.outOf;

					done.append(*it);
				}

				handleSearchResult(hist);
				addHistory(hist);
				return;
			}
		}

		regexp = kanjiSearchItems();
	}

	if (inResults)
		doSearchInResults(text, regexp);
	else
		doSearch(text, regexp);
}

void TopLevel::strokeSearch()
{
	TQString strokesString;

	bool ok = false;
	TQString text = Edit->text().stripWhiteSpace();
	unsigned int strokes = text.toUInt(&ok);

	if (!ok)
	{
		/*
		if (text.find("-") < 0)
		{
			StatusBar->message(i18n("For a range between 4 and 8 strokes, use '4-8'"));
			return;
		}

		unsigned int first = text.section('-', 0, 0).toUInt(&ok);
		if (!ok)
		{
			StatusBar->message(i18n("First number not parseable\n"));
			return;
		}

		unsigned int second = text.section('-', 1, 1).toUInt(&ok);
		if (!ok)
		{
			StatusBar->message(i18n("Second number not parseable\n"));
			return;
		}

		bool already = false;
		for (int i = first; i <= second; ++i)
		{
			if (already)
				strokesString.append('|');

			already = true;

			strokesString.append(TQString::number(i));
		}

		strokesString.append(')');
		strokesString.prepend("(?:");

		//kdDebug() << "strokesString is " << strokesString << endl;
		*/

		StatusBar->message(i18n("Unparseable number"));
		return;
	}
	else if (strokes <= 0 || strokes > 60)
	{
		StatusBar->message(i18n("Invalid stroke count"));
		return;
	}
	else
	{
		strokesString = TQString::number(strokes);
	}


	TQRegExp regexp = TQRegExp(text);

	// must be in kanjidic mode
	kanjiCB->setChecked(true);

	doSearch(TQString("S%1 ").arg(strokesString), regexp);
}

void TopLevel::gradeSearch()
{
	TQString editText = Edit->text().stripWhiteSpace();
	unsigned int grade;

	if (editText.lower() == "jouyou")
		grade = 8;
	else if (editText.lower() == "jinmeiyou")
		grade = 9;
	else
		grade = editText.toUInt();

	if (grade <= 0 || grade > 9)
	{
		StatusBar->message(i18n("Invalid grade"));
		return;
	}

	TQString text = TQString("G%1 ").arg(grade);
	TQRegExp regexp = TQRegExp(text);

	kanjiCB->setChecked(true);

	doSearch(text, regexp);
}

TQString TopLevel::clipBoardText() // gets text from clipboard for globalaccels
{
	kapp->clipboard()->setSelectionMode(true);
	TQString text = kapp->clipboard()->text().stripWhiteSpace();
	kapp->clipboard()->setSelectionMode(false);

	return text;
}

TQString TopLevel::filteredClipboardText()
{
	TQString newText = clipBoardText();
	TQString currentText = Edit->text();
	if (newText.length() < 80 && newText.find(':') < 0 && newText.find('#') < 0 && newText.find("-") != 0 && newText.find("+") < 0 && currentText.find(newText) < 0)
		return newText;
	else
		return TQString();
}

void TopLevel::autoSearch()
{
	if (autoSearchToggle->isChecked())
		searchAccel();
}

void TopLevel::searchAccel()
{
	TQString newText = filteredClipboardText();
	if (!newText.isNull())
	{
		kanjiCB->setChecked(false);

		raise();

		Edit->setText(newText);
		search();
	}
}

void TopLevel::kanjiSearchAccel()
{
	TQString newText = filteredClipboardText();
	if (!newText.isNull())
	{
		kanjiCB->setChecked(true);

		raise();

		Edit->setText(newText);
		search();
	}
}

void TopLevel::setResults(unsigned int results, unsigned int fullNum)
{
	TQString str = i18n("%n result","%n results",results);

	if (results < fullNum)
		str += i18n(" out of %1").arg(fullNum);

	StatusBar->message(str);
	setCaption(str);
}

void TopLevel::slotConfigurationChanged()
{
	updateConfiguration();
}

void TopLevel::updateConfiguration()
{
	_Asyndeta.readKitenConfiguration();
	_ResultView->updateFont();
}

void TopLevel::slotConfigure()
{
	if (ConfigureDialog::showDialog("settings"))
		return;
	
	//ConfigureDialog didn't find an instance of this dialog, so lets create it :
	optionDialog = new ConfigureDialog(this, "settings");
	connect(optionDialog, TQT_SIGNAL(hidden()),this,TQT_SLOT(slotConfigureHide()));
	connect(optionDialog, TQT_SIGNAL(settingsChanged()), TQT_TQOBJECT(this), TQT_SLOT(slotConfigurationChanged()));
	connect(optionDialog, TQT_SIGNAL(valueChanged()), TQT_TQOBJECT(this), TQT_SIGNAL(quizConfChanged()));
	optionDialog->show();

	return;
}

void TopLevel::slotLearnConfigure()
{
	slotConfigure();
	optionDialog->showPage(2);
}

void TopLevel::slotConfigureHide()
{
	TQTimer::singleShot(0, TQT_TQOBJECT(this), TQT_SLOT(slotConfigureDestroy()));
}

void TopLevel::slotConfigureDestroy()
{
	if (optionDialog != 0 && optionDialog->isVisible() == 0)
	{
		delete optionDialog;
		optionDialog = 0;
	}
}

void TopLevel::createLearn()
{
	Learn *_Learn = new Learn(_Asyndeta.retrieveIndex(), 0);

	connect(_Learn, TQT_SIGNAL(destroyed(Learn *)), TQT_TQOBJECT(this), TQT_SLOT(learnDestroyed(Learn *)));
	connect(_Learn, TQT_SIGNAL(linkClicked(const TQString &)), TQT_TQOBJECT(this), TQT_SLOT(ressearch(const TQString &)));
	connect(_Learn, TQT_SIGNAL(configureLearn()), TQT_TQOBJECT(this), TQT_SLOT(slotLearnConfigure()));
	connect(this, TQT_SIGNAL(quizConfChanged()), _Learn, TQT_SLOT(updateQuizConfiguration()));
	connect(this, TQT_SIGNAL(add(Dict::Entry)), _Learn, TQT_SLOT(add(Dict::Entry)));

	learnList.append(_Learn);

	_Learn->show();
}

void TopLevel::learnDestroyed(Learn *learn)
{
	learnList.remove(learn);
}

void TopLevel::createEEdit()
{
	eEdit *_eEdit = new eEdit(_Asyndeta.personalDictionaryLocation(), this);
	_eEdit->show();
}

void TopLevel::kanjiDictChange()
{
	// Do we even *need* something here?
}

TQRegExp TopLevel::readingSearchItems(bool kanji)
{
	TQString text = Edit->text();
	if (text.isEmpty()) // abandon search
		return TQRegExp(); //empty

	//CompletionObj->addItem(text);
	TQString regexp;
	if (kanji)
		regexp = " %1 ";
	else
		regexp = "\\[%1\\]";

	regexp = regexp.arg(text);

	return TQRegExp(regexp, Config::caseSensitive());
}

TQRegExp TopLevel::kanjiSearchItems(bool beginning)
{
	TQString text = Edit->text();

	if (text.isEmpty())
		return TQRegExp(); //empty

	TQString regexp;
	if (beginning)
		regexp = "^%1";
	else
		regexp = "^%1\\W";

	regexp = regexp.arg(text);

	return TQRegExp(regexp, Config::caseSensitive());
}

TQRegExp TopLevel::searchItems()
{
	TQString regexp;
	TQString text = Edit->text();
	if (text.isEmpty())
		return TQRegExp(); //empty

	unsigned int contains = text.contains(TQRegExp("[A-Za-z0-9_:]"));
	if (Config::wholeWord() && contains == text.length())
		regexp = "\\W%1\\W";
	else
		regexp = "%1";

	regexp = regexp.arg(text);

	return TQRegExp(regexp, Config::caseSensitive());
}

void TopLevel::toggleCom()
{
}

void TopLevel::configureToolBars()
{
	saveMainWindowSettings(TDEGlobal::config(), "TopLevelWindow");
	KEditToolbar dlg(actionCollection(), "kitenui.rc");
	connect(&dlg, TQT_SIGNAL(newToolbarConfig()), TQT_SLOT(newToolBarConfig()));
	dlg.exec();
}

void TopLevel::newToolBarConfig()
{
	createGUI("kitenui.rc");
	applyMainWindowSettings(TDEGlobal::config(), "TopLevelWindow");
}

RadWidget *TopLevel::radicalSearch()
{
	RadWidget *rw = new RadWidget(&_Rad, 0, "rw");
	connect(rw, TQT_SIGNAL(set(const TQStringList &, unsigned int, unsigned int)), TQT_TQOBJECT(this), TQT_SLOT(radSearch(const TQStringList &, unsigned int, unsigned int)));
	rw->show();
	return rw;
}

void TopLevel::radSearch(const TQStringList &_list, unsigned int strokes, unsigned int errorMargin)
{
	//kdDebug() << "TopLevel::radSearch\n";

	TQStringList list(_Rad.kanjiByRad(_list));

	TQStringList::iterator it;

	Dict::SearchResult hist;
	hist.count = 0;
	hist.outOf = 0;
	hist.common = comCB->isChecked();

	TQString prettyRadicalString;
	bool already = false;
	for (TQStringList::ConstIterator it = _list.begin(); it != _list.end(); ++it)
	{
		if (already)
			prettyRadicalString.append(", ");
		prettyRadicalString.append(*it);

		already = true;
	}

	hist.text = i18n("Radical(s): %1").arg(prettyRadicalString);

	if (strokes)
		hist.list.append(Dict::Entry(i18n("Kanji with radical(s) %1 and %2 strokes").arg(prettyRadicalString).arg(strokes), true));
	else
		hist.list.append(Dict::Entry(i18n("Kanji with radical(s) %1").arg(prettyRadicalString), true));

	TQString strokesString;
	if (strokes)
	{
		strokesString = TQString::number(strokes);
		for (unsigned i = 1; i <= errorMargin; ++i)
		{
			strokesString.append('|');
			strokesString.prepend('|');
			strokesString.append(TQString::number(strokes + i));
			strokesString.prepend(TQString::number(strokes - i));
		}

		strokesString.append(')');
		strokesString.prepend("(?:");

		//kdDebug() << "strokesString is " << strokesString << endl;
	}

	for (it = list.begin(); it != list.end(); ++it)
	{

		Dict::SearchResult results = _Asyndeta.retrieveIndex()->searchKanji(TQRegExp(strokes? (TQString("S%1 ").arg(strokesString)) : (TQString("^") + (*it)) ), (*it), comCB->isChecked());
		hist.outOf += results.outOf;

		if (results.count < 1)
			continue;

		hist.list.append(Dict::firstEntry(results));
		hist.results.append(Dict::firstEntryText(results));
		hist.count += results.count;
	}

	addHistory(hist);
	handleSearchResult(hist);
}

void TopLevel::back(void)
{
	assert(currentResult != resultHistory.begin());
	--currentResult;
	--currentResultIndex;
	enableHistoryButtons();
	handleSearchResult(*currentResult);
	historySpotChanged();
}

void TopLevel::forward(void)
{
	assert(currentResult != resultHistory.end());
	++currentResult;
	++currentResultIndex;
	enableHistoryButtons();
	handleSearchResult(*currentResult);
	historySpotChanged();
}

void TopLevel::goInHistory(int index)
{
	currentResult = resultHistory.at(resultHistory.count() - historyAction->items().count() + index);
	currentResultIndex = index;
	enableHistoryButtons();
	handleSearchResult(*currentResult);
	historySpotChanged();
}

void TopLevel::historySpotChanged()
{
	historyAction->setCurrentItem(currentResultIndex);
}

void TopLevel::addHistory(Dict::SearchResult result)
{
	TQStringList newHistoryList = historyAction->items();

	// remove from back till we hit currentResult
	while (resultHistory.fromLast() != currentResult)
	{
		resultHistory.pop_back();
		newHistoryList.pop_back();
	}

	resultHistory.append(result);
	newHistoryList.append(result.text);

	// make history menu a reasonable length
	while (newHistoryList.count() > 20)
		newHistoryList.pop_front();

	historyAction->setItems(newHistoryList);

	currentResult = resultHistory.end();
	--currentResult;
	currentResultIndex = resultHistory.count() - 1;

	historySpotChanged();

	enableHistoryButtons();

	if (resultHistory.size() > 50)
		resultHistory.pop_front();
}

void TopLevel::enableHistoryButtons()
{
	backAction->setEnabled(currentResult != resultHistory.begin());
	forwardAction->setEnabled(++currentResult != resultHistory.end());
	--currentResult;
}

void TopLevel::print()
{
	_ResultView->print((*currentResult).text);
}

void TopLevel::configureGlobalKeys()
{
	KKeyDialog::configure(Accel, this);
}

#include "kiten.moc"
