/** This file is part of Kiten, a KDE Japanese Reference Tool... Copyright (C) 2001 Jason Katz-Brown (C) 2005 Paul Temple 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kiten.h" #include "learn.h" #include "kitenconfig.h" #include "optiondialog.h" #include TopLevel::TopLevel(TQWidget *parent, const char *name) : KMainWindow(parent, name) { #if KDE_VERSION > 305 setStandardToolBarMenuEnabled(true); #endif config = Config::self(); config->readConfig(); Accel = new KGlobalAccel(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, 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, this, TQT_SLOT(searchAccel())); Accel->readSettings(KGlobal::config()); Accel->updateConnections(); _ResultView = new ResultView(true, this, "_ResultView"); setCentralWidget(_ResultView); (void) KStdAction::quit(this, TQT_SLOT(close()), actionCollection()); (void) KStdAction::print(this, TQT_SLOT(print()), actionCollection()); (void) KStdAction::preferences(this, TQT_SLOT(slotConfigure()), actionCollection()); KStdAction::keyBindings(guiFactory(), TQT_SLOT(configureShortcuts()), actionCollection()); (void) new KAction(i18n("&Learn"), "pencil", CTRL+Key_L, this, TQT_SLOT(createLearn()), actionCollection(), "file_learn"); (void) new KAction(i18n("&Dictionary Editor..."), "edit", 0, this, TQT_SLOT(createEEdit()), actionCollection(), "dict_editor"); (void) new KAction(i18n("Ra&dical Search..."), "gear", CTRL+Key_R, this, TQT_SLOT(radicalSearch()), actionCollection(), "search_radical"); Edit = new EditAction(i18n("Search Edit"), 0, this, TQT_SLOT(search()), actionCollection(), "search_edit"); (void) new KAction(i18n("&Clear Search Bar"), BarIcon("locationbar_erase", 16), CTRL+Key_N, Edit, TQT_SLOT(clear()), actionCollection(), "clear_search"); (void) new KAction(i18n("S&earch"), "key_enter", 0, this, TQT_SLOT(search()), actionCollection(), "search"); (void) new KAction(i18n("Search with &Beginning of Word"), 0, this, TQT_SLOT(searchBeginning()), actionCollection(), "search_beginning"); (void) new KAction(i18n("Search &Anywhere"), 0, this, TQT_SLOT(searchAnywhere()), actionCollection(), "search_anywhere"); (void) new KAction(i18n("Stro&kes"), "paintbrush", CTRL+Key_S, this, TQT_SLOT(strokeSearch()), actionCollection(), "search_stroke"); (void) new KAction(i18n("&Grade"), "leftjust", CTRL+Key_G, this, TQT_SLOT(gradeSearch()), actionCollection(), "search_grade"); kanjiCB = new KToggleAction(i18n("&Kanjidic"), "kanjidic", CTRL+Key_K, this, TQT_SLOT(kanjiDictChange()), actionCollection(), "kanji_toggle"); deinfCB = new KToggleAction(i18n("&Deinflect Verbs in Regular Search"), 0, this, TQT_SLOT(kanjiDictChange()), actionCollection(), "deinf_toggle"); comCB = new KToggleAction(i18n("&Filter Rare"), "filter", CTRL+Key_F, this, TQT_SLOT(toggleCom()), actionCollection(), "common"); autoSearchToggle = new KToggleAction(i18n("&Automatically Search Clipboard Selections"), "find", 0, this, TQT_SLOT(kanjiDictChange()), actionCollection(), "autosearch_toggle"); irAction = new KAction(i18n("Search &in Results"), "find", CTRL+Key_I, this, TQT_SLOT(resultSearch()), actionCollection(), "search_in_results"); (void) KStdAction::configureToolbars(this, TQT_SLOT(configureToolBars()), actionCollection()); addAction = new KAction(i18n("Add &Kanji to Learning List"), 0, this, TQT_SLOT(addToList()), actionCollection(), "add"); addAction->setEnabled(false); (void) new KAction(i18n("Configure &Global Shortcuts..."), "configure_shortcuts", 0, this, TQT_SLOT(configureGlobalKeys()), actionCollection(), "options_configure_keybinding"); historyAction = new KListAction(i18n("&History"), 0, 0, 0, actionCollection(), "history"); connect(historyAction, TQT_SIGNAL(activated(int)), this, TQT_SLOT(goInHistory(int))); backAction = KStdAction::back(this, TQT_SLOT(back()), actionCollection()); forwardAction = KStdAction::forward(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(KGlobal::config(), "TopLevelWindow"); connect(_ResultView, TQT_SIGNAL(linkClicked(const TQString &)), TQT_SLOT(ressearch(const TQString &))); connect(kapp->clipboard(), TQT_SIGNAL(selectionChanged()), this, TQT_SLOT(autoSearch())); TQTimer::singleShot(10, 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::null); } bool TopLevel::queryClose() { for (TQPtrListIterator 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(KGlobal::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("

%1

").arg(i18n("HTML Entity: %1").arg(TQString("&#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 it = compounds.list.begin(); it != compounds.list.end(); ++it) { //kdDebug() << "adding " << (*it).kanji() << endl; _ResultView->addResult(*it, common); kapp->processEvents(); } } else { for (TQValueListIterator it = results.list.begin(); it != results.list.end(); ++it) { kapp->processEvents(); _ResultView->addKanjiResult(*it, results.common); } } } else { for (TQValueListIterator 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::null; } 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()), this, TQT_SLOT(slotConfigurationChanged())); connect(optionDialog, TQT_SIGNAL(valueChanged()), this, TQT_SIGNAL(quizConfChanged())); optionDialog->show(); return; } void TopLevel::slotLearnConfigure() { slotConfigure(); optionDialog->showPage(2); } void TopLevel::slotConfigureHide() { TQTimer::singleShot(0, 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 *)), this, TQT_SLOT(learnDestroyed(Learn *))); connect(_Learn, TQT_SIGNAL(linkClicked(const TQString &)), this, TQT_SLOT(ressearch(const TQString &))); connect(_Learn, TQT_SIGNAL(configureLearn()), 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(KGlobal::config(), "TopLevelWindow"); KEditToolbar dlg(actionCollection(), "kitenui.rc"); connect(&dlg, TQT_SIGNAL(newToolbarConfig()), TQT_SLOT(newToolBarConfig())); dlg.exec(); } void TopLevel::newToolBarConfig() { createGUI("kitenui.rc"); applyMainWindowSettings(KGlobal::config(), "TopLevelWindow"); } RadWidget *TopLevel::radicalSearch() { RadWidget *rw = new RadWidget(&_Rad, 0, "rw"); connect(rw, TQT_SIGNAL(set(const TQStringList &, unsigned int, unsigned int)), 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"