File indexing completed on 2023-05-30 10:42:10

0001 /*
0002     This file is part of Kiten, a KDE Japanese Reference Tool...
0003     SPDX-FileCopyrightText: 2001 Jason Katz-Brown <jason@katzbrown.com>
0004     SPDX-FileCopyrightText: 2005 Paul Temple <paul.temple@gmx.net>
0005     SPDX-FileCopyrightText: 2006 Joseph Kerian <jkerian@gmail.com>
0006     SPDX-FileCopyrightText: 2006 Eric Kjeldergaard <kjelderg@gmail.com>
0007     SPDX-FileCopyrightText: 2011 Daniel E. Moctezuma <democtezuma@gmail.com>
0008 
0009     SPDX-License-Identifier: GPL-2.0-or-later
0010 */
0011 
0012 #include "kiten.h"
0013 
0014 #include <KActionCollection>
0015 #include <KConfig>
0016 #include <KConfigGui>
0017 #include <KEditToolBar>
0018 #include <KLocalizedString>
0019 #include <KProcess>
0020 #include <KShortcutsDialog>
0021 #include <KStandardAction>
0022 #include <KStandardGuiItem>
0023 #include <KStatusNotifierItem>
0024 #include <KToggleAction>
0025 #include <KXMLGUIFactory>
0026 
0027 #include <QAction>
0028 #include <QApplication>
0029 #include <QClipboard>
0030 #include <QDockWidget>
0031 #include <QFile>
0032 #include <QList>
0033 #include <QPair>
0034 #include <QScrollBar>
0035 #include <QStandardPaths>
0036 #include <QStatusBar>
0037 #include <QTimer>
0038 #include <QVBoxLayout>
0039 
0040 #include "configuredialog.h"
0041 #include "dictionaryupdatemanager.h"
0042 #include "entrylist.h"
0043 #include "entrylistmodel.h"
0044 #include "entrylistview.h"
0045 #include "kitenconfig.h"
0046 #include "resultsview.h"
0047 #include "searchstringinput.h"
0048 /* Separating Learn */
0049 // #include "learn.h"
0050 
0051 Kiten::Kiten(QWidget *parent, const char *name)
0052     : KXmlGuiWindow(parent)
0053     , _lastQuery(DictQuery())
0054     , _radselect_proc(new KProcess(this))
0055     , _kanjibrowser_proc(new KProcess(this))
0056 {
0057     _radselect_proc->setProgram(QStandardPaths::findExecutable(QStringLiteral("kitenradselect")));
0058     _kanjibrowser_proc->setProgram(QStandardPaths::findExecutable(QStringLiteral("kitenkanjibrowser")));
0059     setStandardToolBarMenuEnabled(true);
0060     setObjectName(QLatin1String(name));
0061 
0062     /* Set up the config */
0063     _config = KitenConfigSkeleton::self();
0064     _config->load();
0065 
0066     /* ResultsView is our main widget, displaying the results of a search */
0067     _mainView = new ResultsView(this, "mainView");
0068 
0069     /* Create the export list */
0070     //  setupExportListDock();
0071 
0072     /* TODO: have a look at this idea
0073     detachedView = new ResultsView( NULL, "detachedView" );
0074     */
0075 
0076     setCentralWidget(_mainView);
0077 
0078     setupActions();
0079     // Be sure to create this manager before creating the GUI
0080     // as it needs to add a KAction to it.
0081     _dictionaryUpdateManager = new DictionaryUpdateManager(this);
0082 
0083     createGUI();
0084 
0085     _statusBar = statusBar();
0086     _optionDialog = nullptr;
0087 
0088     /* Start the system tray icon. */
0089     _sysTrayIcon = new KStatusNotifierItem(this);
0090     _sysTrayIcon->setStandardActionsEnabled(true);
0091     _sysTrayIcon->setAssociatedWidget(this);
0092     _sysTrayIcon->setIconByName(QStringLiteral("kiten"));
0093     _sysTrayIcon->setStatus(KStatusNotifierItem::Active);
0094 
0095     /* Set things as they were (as told in the config) */
0096     _autoSearchToggle->setChecked(_config->autosearch());
0097     _inputManager->setDefaultsFromConfig();
0098     updateConfiguration();
0099     applyMainWindowSettings(KSharedConfig::openConfig()->group("kitenWindow"));
0100 
0101     /* What happens when links are clicked or things are selected in the clipboard */
0102     connect(_mainView, &ResultsView::urlClicked, this, &Kiten::searchText);
0103     connect(QApplication::clipboard(), &QClipboard::selectionChanged, this, &Kiten::searchClipboard);
0104     connect(_inputManager, &SearchStringInput::search, this, &Kiten::searchFromEdit);
0105 
0106     connect(_mainView->verticalScrollBar(), &QAbstractSlider::valueChanged, this, &Kiten::setCurrentScrollValue);
0107     /* We need to know when to reload our dictionaries if the user updated them. */
0108     connect(_dictionaryUpdateManager, &DictionaryUpdateManager::updateFinished, this, &Kiten::loadDictionaries);
0109 
0110     /* See below for what else needs to be done */
0111     QTimer::singleShot(10, this, &Kiten::finishInit);
0112 }
0113 
0114 // Destructor to clean up the little bits
0115 Kiten::~Kiten()
0116 {
0117     if (_radselect_proc->state() != QProcess::NotRunning) {
0118         _radselect_proc->kill();
0119     }
0120     if (_kanjibrowser_proc->state() != QProcess::NotRunning) {
0121         _kanjibrowser_proc->kill();
0122     }
0123     _dictionaryManager.removeAllDictionaries();
0124     delete _optionDialog;
0125     _optionDialog = nullptr;
0126 }
0127 
0128 KitenConfigSkeleton *Kiten::getConfig()
0129 {
0130     return _config;
0131 }
0132 
0133 void Kiten::setupActions()
0134 {
0135     /* Add the basic quit/print/prefs actions, use the gui factory for keybindings */
0136     (void)KStandardAction::quit(this, SLOT(close()), actionCollection());
0137     // Why the heck is KSA:print adding it's own toolbar!?
0138     //  (void) KStandardAction::print(this, SLOT(print()), actionCollection());
0139     (void)KStandardAction::preferences(this, SLOT(slotConfigure()), actionCollection());
0140     // old style cast seems needed here, (const QObject*)
0141     KStandardAction::keyBindings(guiFactory(), &KXMLGUIFactory::showConfigureShortcutsDialog, actionCollection());
0142 
0143     /* Setup the Go-to-learn-mode actions */
0144     /* TODO: put back when Dictionary Editor is reorganised */
0145     //  (void) new KAction(   i18n( "&Dictionary Editor..." )
0146     //                             , "document-properties"
0147     //                             , 0
0148     //                             , this
0149     //                             , SLOT(createEEdit())
0150     //                             , actionCollection()
0151     //                             , "dict_editor");
0152     QAction *radselect = actionCollection()->addAction(QStringLiteral("radselect"));
0153     radselect->setText(i18n("Radical Selector"));
0154     //  radselect->setIcon( "edit-find" );
0155     actionCollection()->setDefaultShortcut(radselect, Qt::CTRL + Qt::Key_R);
0156     connect(radselect, &QAction::triggered, this, &Kiten::radicalSearch);
0157 
0158     QAction *kanjibrowser = actionCollection()->addAction(QStringLiteral("kanjibrowser"));
0159     kanjibrowser->setText(i18n("Kanji Browser"));
0160     actionCollection()->setDefaultShortcut(kanjibrowser, Qt::CTRL + Qt::Key_K);
0161     connect(kanjibrowser, &QAction::triggered, this, &Kiten::kanjiBrowserSearch);
0162 
0163     /* Setup the Search Actions and our custom Edit Box */
0164     _inputManager = new SearchStringInput(this);
0165 
0166     QAction *searchButton = actionCollection()->addAction(QStringLiteral("search"));
0167     searchButton->setText(i18n("S&earch"));
0168     // Set the search button to search
0169     connect(searchButton, &QAction::triggered, this, &Kiten::searchFromEdit);
0170     searchButton->setIcon(KStandardGuiItem::find().icon());
0171 
0172     // That's not it, that's "find as you type"...
0173     //   connect( Edit, SIGNAL(completion(QString)),
0174     //            this,   SLOT(searchFromEdit()) );
0175 
0176     /* Setup our widgets that handle preferences */
0177     //   deinfCB = new KToggleAction(   i18n( "&Deinflect Verbs in Regular Search" )
0178     //                                 , 0
0179     //                                 , this
0180     //                                 , SLOT(kanjiDictChange())
0181     //                                 , actionCollection()
0182     //                                 , "deinf_toggle" );
0183 
0184     _autoSearchToggle = actionCollection()->add<KToggleAction>(QStringLiteral("autosearch_toggle"));
0185     _autoSearchToggle->setText(i18n("&Automatically Search Clipboard Selections"));
0186 
0187     _irAction = actionCollection()->add<QAction>(QStringLiteral("search_in_results"));
0188     _irAction->setText(i18n("Search &in Results"));
0189     connect(_irAction, &QAction::triggered, this, &Kiten::searchInResults);
0190 
0191     QAction *actionFocusResultsView;
0192     actionFocusResultsView = actionCollection()->addAction(QStringLiteral("focusresultview"), this, SLOT(focusResultsView()));
0193     actionCollection()->setDefaultShortcut(actionFocusResultsView, Qt::Key_Escape);
0194     actionFocusResultsView->setText(i18n("Focus result view"));
0195 
0196     (void)KStandardAction::configureToolbars(this, SLOT(configureToolBars()), actionCollection());
0197 
0198     // TODO: this should probably be a standard action
0199     /*
0200     globalShortcutsAction = actionCollection()->addAction( "options_configure_global_keybinding" );
0201     globalShortcutsAction->setText( i18n( "Configure &Global Shortcuts..." ) );
0202     connect( globalShortcutsAction, SIGNAL(triggered()), this, SLOT(configureGlobalKeys()) );
0203     */
0204 
0205     // TODO: implement this
0206     //_globalSearchAction = actionCollection()->add<KToggleAction>( "search_on_the_spot" );
0207     //_globalSearchAction->setText( i18n( "On The Spo&t Search" ) );
0208     // KAction *temp = qobject_cast<KAction*>( _globalSearchAction );
0209     // KShortcut shrt( "Ctrl+Alt+S" );
0210     // globalSearchAction->setGlobalShortcut(shrt);  //FIXME: Why does this take ~50 seconds to return!?
0211     // connect(globalSearchAction, SIGNAL(triggered()), this, SLOT(searchOnTheSpot()));
0212 
0213     _backAction = KStandardAction::back(this, SLOT(back()), actionCollection());
0214     _forwardAction = KStandardAction::forward(this, SLOT(forward()), actionCollection());
0215     _backAction->setEnabled(false);
0216     _forwardAction->setEnabled(false);
0217 }
0218 
0219 void Kiten::setupExportListDock()
0220 {
0221     _exportListDock = new QDockWidget(i18n("Export List"), this);
0222     _exportListDockContents = new QWidget(_exportListDock);
0223     _exportListDock->setWidget(_exportListDockContents);
0224     addDockWidget(Qt::RightDockWidgetArea, _exportListDock);
0225 
0226     QVBoxLayout *layout = new QVBoxLayout(_exportListDockContents);
0227     _exportListDockContents->setLayout(layout);
0228 
0229     _exportList = new EntryListView(_exportListDockContents);
0230 
0231     layout->addWidget(_exportList);
0232 
0233     _exportList->setModel(new EntryListModel(EntryList()));
0234 }
0235 
0236 void Kiten::addExportListEntry(int index)
0237 {
0238     EntryListModel *model = qobject_cast<EntryListModel *>(_exportList->model());
0239     if (!model)
0240         return;
0241 
0242     EntryList list = model->entryList();
0243 
0244     list << _historyList.current()->at(index)->clone();
0245     model->setEntryList(list);
0246 }
0247 
0248 // This is the latter part of the initialization
0249 void Kiten::finishInit()
0250 {
0251     _statusBar->showMessage(i18n("Initializing Dictionaries"));
0252 
0253     // if it's the application's first time starting,
0254     // the app group won't exist and we show demo
0255     if (_config->initialSearch()) {
0256         if (KConfigGui::hasSessionConfig() && !KConfigGui::sessionConfig()->hasGroup("app")) {
0257             searchTextAndRaise(QStringLiteral("辞書"));
0258             // Note to future tinkerers... DO NOT EDIT OR TRANSLATE THAT
0259             // it's an embedded unicode search string to find "dictionary" in japanese
0260         }
0261     }
0262 
0263     _inputManager->focusInput();
0264     _statusBar->showMessage(i18n("Welcome to Kiten"));
0265     setCaption(QString());
0266 }
0267 
0268 void Kiten::focusResultsView()
0269 {
0270     _mainView->verticalScrollBar()->setFocus();
0271 }
0272 
0273 // This function is run on program window close.
0274 // It saves the settings in the config.
0275 bool Kiten::queryClose()
0276 {
0277     _config->setAutosearch(_autoSearchToggle->isChecked());
0278     _config->save();
0279 
0280     KConfigGroup configGroup = KSharedConfig::openConfig()->group("kitenWindow");
0281     saveMainWindowSettings(configGroup);
0282     return true;
0283 }
0284 
0285 //////////////////////////////////////////////////////////////////////////////
0286 // SEARCHING METHODS
0287 //////////////////////////////////////////////////////////////////////////////
0288 
0289 /**
0290  * This function searches for the contents of the Edit field in the mainwindow.
0291  * Any gui choices will also be included here.
0292  */
0293 void Kiten::searchFromEdit()
0294 {
0295     qDebug() << "SEARCH FROM EDIT CALLED";
0296     DictQuery query = _inputManager->getSearchQuery();
0297     if (query != _lastQuery) {
0298         _lastQuery = query;
0299         searchAndDisplay(query);
0300     }
0301 }
0302 
0303 /**
0304  * This function is called when a kanji is clicked in the result view
0305  * or any other time in which we want to search for something that didn't
0306  * come from the input box.
0307  */
0308 void Kiten::searchText(const QString &text)
0309 {
0310     searchAndDisplay(DictQuery(text));
0311 }
0312 
0313 /**
0314  * This should change the Edit text to be appropriate and then begin a search
0315  * of the dictionaries' entries.
0316  */
0317 void Kiten::searchTextAndRaise(const QString &str)
0318 {
0319     /* Do the search */
0320     searchText(str);
0321     /* get the window as we'd like it */
0322     raise();
0323 }
0324 
0325 /**
0326  * This function will search things as they are put in the clipboard.
0327  * It checks each time to see if the autoSearchToggle is set.
0328  * This function searches for the contents of the clipboard
0329  * filter out long selections and selections that are contained in our
0330  * current search (alleviates problem where research occurs because of
0331  * X's auto-add-to-clipboard-on-select feature.
0332  */
0333 void Kiten::searchClipboard()
0334 {
0335     if (_autoSearchToggle->isChecked()) {
0336         QString clipboard = QApplication::clipboard()->text(QClipboard::Selection).simplified();
0337 
0338         // Only search if the clipboard selection was less than 20 characters wide
0339         if (clipboard.length() < 20) {
0340             DictQuery potentialQuery(clipboard);
0341             // Make sure that we're not looking for a substring of the current string (needed?)
0342             if (!(potentialQuery < _inputManager->getSearchQuery())) {
0343                 searchTextAndRaise(clipboard);
0344             }
0345 
0346             qDebug() << "Clipboard item is a substring (rejected)";
0347         } else {
0348             qDebug() << "Clipboard entry too long";
0349         }
0350     }
0351 }
0352 
0353 /**
0354  * This method performs the search and displays
0355  * the result to the screen.
0356  */
0357 void Kiten::searchAndDisplay(const DictQuery &query)
0358 {
0359     /* keep the user informed of what we are doing */
0360     _statusBar->showMessage(i18n("Searching..."));
0361 
0362     /* This gorgeous incantation is all that's necessary to fill a DictQuery
0363       with a query and an Entrylist with all of the results form all of the
0364       requested dictionaries */
0365     EntryList *results = _dictionaryManager.doSearch(query);
0366 
0367     /* if there are no results */
0368     if (results->size() == 0) // TODO: check here if the user actually prefers this
0369     {
0370         // create a modifiable copy of the original query
0371         DictQuery newQuery(query);
0372 
0373         bool tryAgain = false;
0374 
0375         do {
0376             // by default we don't try again
0377             tryAgain = false;
0378 
0379             // but if the matchtype is changed we try again
0380             if (newQuery.getMatchType() == DictQuery::Exact) {
0381                 newQuery.setMatchType(DictQuery::Beginning);
0382                 tryAgain = true;
0383             } else if (newQuery.getMatchType() == DictQuery::Beginning) {
0384                 newQuery.setMatchType(DictQuery::Anywhere);
0385                 tryAgain = true;
0386             }
0387 
0388             // try another search
0389             if (tryAgain) {
0390                 delete results;
0391                 results = _dictionaryManager.doSearch(newQuery);
0392 
0393                 // results means all is ok; don't try again
0394                 if (results->size() > 0) {
0395                     tryAgain = false;
0396                 }
0397             }
0398         } while (tryAgain);
0399     }
0400 
0401     /* synchronize the history (and store this pointer there) */
0402     addHistory(results);
0403 
0404     /* Add the current search to our drop down list */
0405     _inputManager->setSearchQuery(results->getQuery());
0406 
0407     /* suppose it's about time to show the users the results. */
0408     displayResults(results);
0409 }
0410 
0411 /**
0412  * Search in the previous results, identical to
0413  * searchAndDisplay except for the one call.
0414  */
0415 void Kiten::searchInResults()
0416 {
0417     _statusBar->showMessage(i18n("Searching..."));
0418 
0419     DictQuery searchQuery = _inputManager->getSearchQuery();
0420     EntryList *results = _dictionaryManager.doSearchInList(searchQuery, _historyList.current());
0421 
0422     addHistory(results);
0423     _inputManager->setSearchQuery(searchQuery);
0424     displayResults(results);
0425 }
0426 
0427 /**
0428  * Given a set of Search Results items, this function does all that's needed
0429  * to put the interface into an appropriate state for those searchResults.
0430  */
0431 void Kiten::displayResults(EntryList *results)
0432 {
0433     QString infoStr;
0434     /* synchronize the statusbar */
0435     if (results->count() > 0) {
0436         infoStr = i18np("Found 1 result", "Found %1 results", results->count());
0437     } else {
0438         infoStr = i18n("No results found");
0439     }
0440     _statusBar->showMessage(infoStr);
0441     setCaption(infoStr);
0442 
0443     /* sort the results */
0444     /* synchronize the results window */
0445     if (results->count() > 0) {
0446         QStringList dictSort;
0447         QStringList fieldSort = _config->field_sortlist();
0448         if (_config->dictionary_enable() == QLatin1String("true")) {
0449             dictSort = _config->dictionary_sortlist();
0450         }
0451         results->sort(fieldSort, dictSort);
0452         _mainView->setContents(results->toHTML());
0453     } else {
0454         _mainView->setContents("<html><body>" + infoStr + "</body></html>");
0455     }
0456 
0457     _mainView->setLaterScrollValue(results->scrollValue());
0458 
0459     /* //Debuggery: to print the html results to file:
0460     QFile file( "/tmp/lala" );
0461     file.open( QIODevice::WriteOnly );
0462     file.write( results->toHTML().toUtf8() );
0463     file.close();
0464     */
0465 }
0466 
0467 /*
0468 void Kiten::searchOnTheSpot()
0469 {
0470   qDebug() << "On the spot search!\n";
0471 }
0472 */
0473 
0474 void Kiten::radicalSearch()
0475 {
0476     // Radselect is a KUniqueApplication, so we don't
0477     // need to worry about opening more than one copy
0478     _radselect_proc->start();
0479 }
0480 
0481 void Kiten::kanjiBrowserSearch()
0482 {
0483     // KanjiBrowser is a KUniqueApplication, so we don't
0484     // need to worry about opening more than one copy
0485     _kanjibrowser_proc->start();
0486 }
0487 
0488 //////////////////////////////////////////////////////////////////////////////
0489 // PREFERENCES RELATED METHODS
0490 //////////////////////////////////////////////////////////////////////////////
0491 void Kiten::slotConfigure()
0492 {
0493     // If the settings dialog is open and obscured/hidden/cached, show it
0494     if (ConfigureDialog::showDialog(QStringLiteral("settings"))) {
0495         return;
0496     }
0497 
0498     // ConfigureDialog didn't find an instance of this dialog, so lets create it :
0499     _optionDialog = new ConfigureDialog(this, _config);
0500     connect(_optionDialog, SIGNAL(hidden()), this, SLOT(slotConfigureHide()));
0501     connect(_optionDialog, &ConfigureDialog::settingsChanged, this, &Kiten::updateConfiguration);
0502 
0503     _optionDialog->show();
0504 }
0505 
0506 /**
0507  * This function just queues up slotConfigureDestroy() to get around the
0508  * SIGSEGV if you try to delete yourself if you are in the stack.
0509  */
0510 void Kiten::slotConfigureHide()
0511 {
0512     QTimer::singleShot(0, this, &Kiten::slotConfigureDestroy);
0513 }
0514 
0515 /**
0516  * This function actually tears down the optionDialog
0517  */
0518 void Kiten::slotConfigureDestroy()
0519 {
0520     if (_optionDialog != nullptr && _optionDialog->isVisible() == 0) {
0521         delete _optionDialog;
0522         _optionDialog = nullptr;
0523     }
0524 }
0525 
0526 /* TODO: reimplement something very much like this
0527 void Kiten::createEEdit()
0528 {
0529   eEdit *_eEdit = new eEdit( PERSONALDictionaryLocation( "data" ), this );
0530   _eEdit->show();
0531 }
0532 */
0533 
0534 void Kiten::configureToolBars()
0535 {
0536     KConfigGroup configGroup = KSharedConfig::openConfig()->group("kitenWindow");
0537     saveMainWindowSettings(configGroup);
0538     KEditToolBar dlg(actionCollection());
0539     connect(&dlg, &KEditToolBar::newToolBarConfig, this, &Kiten::newToolBarConfig);
0540     dlg.exec();
0541 }
0542 
0543 void Kiten::newToolBarConfig()
0544 {
0545     createGUI(QStringLiteral("kitenui.rc"));
0546     KConfigGroup configGroup = KSharedConfig::openConfig()->group("kitenWindow");
0547     applyMainWindowSettings(configGroup);
0548 }
0549 
0550 /** Opens the dialog for configuring the global accelerator keys. */
0551 void Kiten::configureGlobalKeys()
0552 {
0553     KShortcutsDialog::showDialog(actionCollection(), KShortcutsEditor::LetterShortcutsAllowed, this);
0554 }
0555 
0556 /**
0557  * This function, as the name says, updates the configuration file.
0558  * It should be called in EVERY case where the configuration files change.
0559  */
0560 void Kiten::updateConfiguration()
0561 {
0562     loadDictionaries();
0563 
0564     // Update the HTML/CSS for our fonts
0565     displayHistoryItem();
0566 
0567     _inputManager->updateFontFromConfig();
0568 
0569     // Reset the content of the last query. This is because in case the user adds
0570     // new dictionaries and wants to execute the same last search, the output
0571     // will contain results of the new dictionary/dictionaries added.
0572     _lastQuery = DictQuery();
0573 
0574     /*: TODO: have a look at this as well
0575     detachedView->updateFont();
0576     */
0577 }
0578 
0579 /**
0580  * Loads the dictionaries, their settings and updates general
0581  * options for the display manager.
0582  */
0583 void Kiten::loadDictionaries()
0584 {
0585     // Avoid duplicates (this makes it easy when we need to reload the dictionaries).
0586     _dictionaryManager.removeAllDictionaries();
0587 
0588     // Load the dictionaries of each type that we can adjust in prefs
0589     foreach (const QString &it, _config->dictionary_list()) {
0590         loadDictConfig(it);
0591     }
0592 
0593     // Load settings for each dictionary type
0594     foreach (const QString &it, _dictionaryManager.listDictFileTypes()) {
0595         _dictionaryManager.loadDictSettings(it, _config);
0596     }
0597 
0598     // Update general options for the display manager (sorting by dict, etc)
0599     _dictionaryManager.loadSettings(*_config->config());
0600     qDebug() << "Dictionaries loaded!";
0601 }
0602 
0603 /**
0604  * This function loads the dictionaries from the config file for the program
0605  * to use via the dictionaryManager object.
0606  */
0607 void Kiten::loadDictConfig(const QString &dictType)
0608 {
0609     KConfigGroup group = _config->config()->group("dicts_" + dictType.toLower());
0610 
0611     // A list of QPair's Name->Path
0612     QList<QPair<QString, QString>> dictionariesToLoad;
0613 
0614     // If we need to load the global
0615     if (group.readEntry("__useGlobal", true)) {
0616         QString dictionary = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kiten/") + dictType.toLower());
0617         dictionariesToLoad.append(qMakePair(dictType, dictionary));
0618     }
0619 
0620     QStringList dictNames = group.readEntry<QStringList>("__NAMES", QStringList());
0621     foreach (const QString &name, dictNames) {
0622         QString dictPath = group.readEntry(name, QString());
0623         if (!dictPath.isEmpty() && !name.isEmpty()) {
0624             dictionariesToLoad.append(qMakePair(name, dictPath));
0625         }
0626     }
0627 
0628     QStringList loadedDictionaries = _dictionaryManager.listDictionariesOfType(dictType.toLower());
0629 
0630     typedef QPair<QString, QString> __dictName_t; // Can't have commas in a foreach
0631     foreach (const __dictName_t &it, dictionariesToLoad) {
0632         // Remove from the loadedDictionaries list all the dicts that we are supposed to load
0633         // This will leave only those that need to be unloaded at the end
0634         if (loadedDictionaries.removeAll(it.first) == 0) {
0635             _dictionaryManager.addDictionary(it.second, it.first, dictType.toLower());
0636         }
0637     }
0638 
0639     foreach (const QString &it, loadedDictionaries) {
0640         _dictionaryManager.removeDictionary(it);
0641     }
0642 
0643     /*
0644     #define PERSONALDictionaryLocation( __X ) KGlobal::dirs()->saveLocation(__X, \
0645             "kiten/dictionaries/",true).append("personal")
0646     if(!isKanji) { //Don't load personal dicts as kanji dicts
0647             QString personalDict(PERSONALDictionaryLocation("data"));
0648             if (QFile::exists(personalDict) &&
0649                                     loadedDicts.find(personalDict) == loadedDicts.end())
0650             {
0651                     dictionaryManager.addDictionary(personalDict,"Personal",dictType.lower());
0652             }
0653     }
0654     */
0655 }
0656 
0657 /**
0658  * This function allows one to print out the currently displayed result
0659  */
0660 void Kiten::print()
0661 {
0662     //  _ResultsView->toHTML();
0663 }
0664 
0665 /******************************************************************************
0666   HISTORY HANDLING FUNCTIONS
0667 ******************************************************************************/
0668 
0669 /**
0670  * Given one Search Result, it adds it to the history list the logic in it
0671  * exists to maintain the history list of a certain size.  Note that this method
0672  * does not display the EntryList it is given... so you can add something to the
0673  * history and display it separately.
0674  */
0675 void Kiten::addHistory(EntryList *result)
0676 {
0677     _historyList.addItem(result);
0678     enableHistoryButtons();
0679 }
0680 
0681 /**
0682  * This goes back one item in the history and displays
0683  */
0684 void Kiten::back(void)
0685 {
0686     _historyList.prev();
0687     displayHistoryItem();
0688 }
0689 
0690 /**
0691  * This goes forward one item in the history and displays
0692  */
0693 void Kiten::forward(void)
0694 {
0695     _historyList.next();
0696     displayHistoryItem();
0697 }
0698 
0699 /**
0700  * This method just sets the current element in the list and triggers the display
0701  */
0702 void Kiten::goInHistory(int index)
0703 {
0704     _historyList.setCurrent(index);
0705     displayHistoryItem();
0706 }
0707 
0708 void Kiten::displayHistoryItem()
0709 {
0710     if (_historyList.current() == nullptr)
0711         return;
0712 
0713     _inputManager->setSearchQuery(_historyList.current()->getQuery());
0714     enableHistoryButtons();
0715 
0716     displayResults(_historyList.current());
0717 }
0718 
0719 /**
0720  * This function determines whether the forward and back buttons should be enabled.
0721  * It is currently done independently of what action has just occurred.
0722  */
0723 void Kiten::enableHistoryButtons()
0724 {
0725     _backAction->setEnabled(_historyList.index() > 0);
0726     _forwardAction->setEnabled(_historyList.index() + 1 < _historyList.count());
0727 }
0728 
0729 void Kiten::setCurrentScrollValue(int value)
0730 {
0731     if (_historyList.current() == nullptr)
0732         return;
0733 
0734     _historyList.current()->setScrollValue(value);
0735 }