"Fossies" - the Fresh Open Source Software Archive

Member "recoll-1.26.3/qtgui/spell_w.cpp" (4 Sep 2019, 14976 Bytes) of package /linux/privat/recoll-1.26.3.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "spell_w.cpp" see the Fossies "Dox" file reference documentation.

    1 /* Copyright (C) 2005 J.F.Dockes
    2  *   This program is free software; you can redistribute it and/or modify
    3  *   it under the terms of the GNU General Public License as published by
    4  *   the Free Software Foundation; either version 2 of the License, or
    5  *   (at your option) any later version.
    6  *
    7  *   This program is distributed in the hope that it will be useful,
    8  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
    9  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   10  *   GNU General Public License for more details.
   11  *
   12  *   You should have received a copy of the GNU General Public License
   13  *   along with this program; if not, write to the
   14  *   Free Software Foundation, Inc.,
   15  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
   16  */
   17 #include "autoconfig.h"
   18 
   19 #include <stdio.h>
   20 
   21 #include <algorithm>
   22 #include <list>
   23 #include <map>
   24 #include <string>
   25 
   26 #include <qmessagebox.h>
   27 #include <qpushbutton.h>
   28 #include <qlabel.h>
   29 #include <qlineedit.h>
   30 #include <qlayout.h>
   31 #include <qtooltip.h>
   32 #include <qcombobox.h>
   33 #include <QTableWidget>
   34 #include <QHeaderView>
   35 #include <QClipboard>
   36 #include <QKeyEvent>
   37 
   38 #include "log.h"
   39 #include "recoll.h"
   40 #include "spell_w.h"
   41 #include "guiutils.h"
   42 #include "rcldb.h"
   43 #include "searchdata.h"
   44 #include "rclquery.h"
   45 #include "rclhelp.h"
   46 #include "wasatorcl.h"
   47 #include "execmd.h"
   48 #include "indexer.h"
   49 #include "fstreewalk.h"
   50 
   51 using std::list;
   52 using std::multimap;
   53 using std::string;
   54 
   55 inline bool wordlessMode(SpellW::comboboxchoice v)
   56 {
   57     return (v == SpellW::TYPECMB_STATS || v == SpellW::TYPECMB_FAILED);
   58 }
   59 
   60 void SpellW::init()
   61 {
   62     m_c2t.clear();
   63     expTypeCMB->addItem(tr("Wildcards"));
   64     m_c2t.push_back(TYPECMB_WILD);
   65     expTypeCMB->addItem(tr("Regexp"));
   66     m_c2t.push_back(TYPECMB_REG);
   67     expTypeCMB->addItem(tr("Stem expansion"));
   68     m_c2t.push_back(TYPECMB_STEM);
   69     expTypeCMB->addItem(tr("Spelling/Phonetic"));
   70     m_c2t.push_back(TYPECMB_SPELL);
   71     expTypeCMB->addItem(tr("Show index statistics"));
   72     m_c2t.push_back(TYPECMB_STATS);
   73     expTypeCMB->addItem(tr("List files which could not be indexed (slow)"));
   74     m_c2t.push_back(TYPECMB_FAILED);
   75 
   76     // Stemming language combobox
   77     stemLangCMB->clear();
   78     vector<string> langs;
   79     if (!getStemLangs(langs)) {
   80     QMessageBox::warning(0, "Recoll", 
   81                  tr("error retrieving stemming languages"));
   82     }
   83     for (vector<string>::const_iterator it = langs.begin(); 
   84      it != langs.end(); it++) {
   85     stemLangCMB->addItem(u8s2qs(*it));
   86     }
   87 
   88     (void)new HelpClient(this);
   89     HelpClient::installMap((const char *)this->objectName().toUtf8(), 
   90                "RCL.SEARCH.GUI.TERMEXPLORER");
   91 
   92     // signals and slots connections
   93     connect(baseWordLE, SIGNAL(textChanged(const QString&)), 
   94         this, SLOT(wordChanged(const QString&)));
   95     connect(baseWordLE, SIGNAL(returnPressed()), this, SLOT(doExpand()));
   96     connect(expandPB, SIGNAL(clicked()), this, SLOT(doExpand()));
   97     connect(dismissPB, SIGNAL(clicked()), this, SLOT(close()));
   98     connect(expTypeCMB, SIGNAL(activated(int)), this, SLOT(onModeChanged(int)));
   99 
  100     resTW->setShowGrid(0);
  101 #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
  102     resTW->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
  103 #else
  104     resTW->horizontalHeader()->setResizeMode(0, QHeaderView::Stretch);
  105 #endif
  106     resTW->verticalHeader()->setDefaultSectionSize(20); 
  107     connect(resTW,
  108        SIGNAL(cellDoubleClicked(int, int)),
  109             this, SLOT(textDoubleClicked(int, int)));
  110 
  111     resTW->setColumnWidth(0, 200);
  112     resTW->setColumnWidth(1, 150);
  113     resTW->installEventFilter(this);
  114 
  115     int idx = cmbIdx((comboboxchoice)prefs.termMatchType);
  116     expTypeCMB->setCurrentIndex(idx);
  117     onModeChanged(idx);
  118 }
  119 
  120 int SpellW::cmbIdx(comboboxchoice mode)
  121 {
  122     vector<comboboxchoice>::const_iterator it = 
  123     std::find(m_c2t.begin(), m_c2t.end(), mode);
  124     if (it == m_c2t.end())
  125     it = m_c2t.begin();
  126     return it - m_c2t.begin();
  127 }
  128 
  129 static const int maxexpand = 10000;
  130 
  131 /* Expand term according to current mode */
  132 void SpellW::doExpand()
  133 {
  134     int idx = expTypeCMB->currentIndex();
  135     if (idx < 0 || idx >= int(m_c2t.size()))
  136     idx = 0;
  137     comboboxchoice mode = m_c2t[idx];
  138 
  139     // Can't clear qt4 table widget: resets column headers too
  140     resTW->setRowCount(0);
  141     if (baseWordLE->text().isEmpty() && !wordlessMode(mode)) 
  142     return;
  143 
  144     string reason;
  145     if (!maybeOpenDb(reason)) {
  146     QMessageBox::critical(0, "Recoll", QString(reason.c_str()));
  147     LOGDEB("SpellW::doExpand: db error: "  << (reason) << "\n" );
  148     return;
  149     }
  150 
  151     int mt;
  152     switch(mode) {
  153     case TYPECMB_WILD: mt = Rcl::Db::ET_WILD; break;
  154     case TYPECMB_REG: mt = Rcl::Db::ET_REGEXP; break;
  155     case TYPECMB_STEM: mt = Rcl::Db::ET_STEM; break;
  156     default: mt = Rcl::Db::ET_WILD;
  157     }
  158     if (caseSensCB->isChecked()) {
  159     mt |= Rcl::Db::ET_CASESENS;
  160     }
  161     if (diacSensCB->isChecked()) {
  162     mt |= Rcl::Db::ET_DIACSENS;
  163     }
  164     Rcl::TermMatchResult res;
  165     string expr = string((const char *)baseWordLE->text().toUtf8());
  166     Rcl::DbStats dbs;
  167     rcldb->dbStats(dbs, false);
  168 
  169     switch (mode) {
  170     case TYPECMB_WILD: 
  171     default:
  172     case TYPECMB_REG:
  173     case TYPECMB_STEM:
  174     {
  175     string l_stemlang = qs2utf8s(stemLangCMB->currentText());
  176 
  177     if (!rcldb->termMatch(mt, l_stemlang, expr, res, maxexpand)) {
  178         LOGERR("SpellW::doExpand:rcldb::termMatch failed\n" );
  179         return;
  180     }
  181         statsLBL->setText(tr("Index: %1 documents, average length %2 terms."
  182                  "%3 results")
  183                           .arg(dbs.dbdoccount).arg(dbs.dbavgdoclen, 0, 'f', 0)
  184               .arg(res.entries.size()));
  185     }
  186         
  187     break;
  188 
  189     case TYPECMB_SPELL: 
  190     {
  191     LOGDEB("SpellW::doExpand: spelling [" << expr << "]\n" );
  192     vector<string> suggs;
  193     if (!rcldb->getSpellingSuggestions(expr, suggs)) {
  194         QMessageBox::warning(0, "Recoll", tr("Spell expansion error. "));
  195     }
  196     for (const auto& it : suggs) {
  197         res.entries.push_back(Rcl::TermMatchEntry(it));
  198         }
  199         statsLBL->setText(tr("%1 results").arg(res.entries.size()));
  200     }
  201     break;
  202 
  203     case TYPECMB_STATS: 
  204     {
  205     showStats();
  206     return;
  207     }
  208     break;
  209     case TYPECMB_FAILED:
  210     {
  211     showFailed();
  212     return;
  213     }
  214     break;
  215     }
  216 
  217     if (res.entries.empty()) {
  218         resTW->setItem(0, 0, new QTableWidgetItem(tr("No expansion found")));
  219     } else {
  220         int row = 0;
  221 
  222     if (maxexpand > 0 && int(res.entries.size()) >= maxexpand) {
  223         resTW->setRowCount(row + 1);
  224         resTW->setSpan(row, 0, 1, 2);
  225         resTW->setItem(row++, 0, 
  226                new QTableWidgetItem(
  227                    tr("List was truncated alphabetically, "
  228                   "some frequent "))); 
  229         resTW->setRowCount(row + 1);
  230         resTW->setSpan(row, 0, 1, 2);
  231         resTW->setItem(row++, 0, new QTableWidgetItem(
  232                    tr("terms may be missing. "
  233                   "Try using a longer root.")));
  234         resTW->setRowCount(row + 1);
  235         resTW->setItem(row++, 0, new QTableWidgetItem(""));
  236     }
  237 
  238     for (vector<Rcl::TermMatchEntry>::iterator it = res.entries.begin(); 
  239          it != res.entries.end(); it++) {
  240         LOGDEB2("SpellW::expand: " << it->wcf << " [" << it->term << "]\n");
  241         char num[30];
  242         if (it->wcf)
  243         sprintf(num, "%d / %d",  it->docs, it->wcf);
  244         else
  245         num[0] = 0;
  246         resTW->setRowCount(row+1);
  247             resTW->setItem(row, 0, new QTableWidgetItem(u8s2qs(it->term)));
  248             resTW->setItem(row++, 1, 
  249                              new QTableWidgetItem(QString::fromUtf8(num)));
  250     }
  251     }
  252 }
  253 
  254 void SpellW::showStats()
  255 {
  256     statsLBL->setText("");
  257     int row = 0;
  258 
  259     Rcl::DbStats res;
  260     if (!rcldb->dbStats(res, false)) {
  261     LOGERR("SpellW::doExpand:rcldb::dbStats failed\n" );
  262     return;
  263     }
  264 
  265     resTW->setRowCount(row+1);
  266     resTW->setItem(row, 0,
  267            new QTableWidgetItem(tr("Number of documents")));
  268     resTW->setItem(row++, 1, new QTableWidgetItem(
  269                QString::number(res.dbdoccount)));
  270 
  271     resTW->setRowCount(row+1);
  272     resTW->setItem(row, 0,
  273            new QTableWidgetItem(tr("Average terms per document")));
  274     resTW->setItem(row++, 1, new QTableWidgetItem(
  275                QString::number(res.dbavgdoclen, 'f', 0)));
  276 
  277     resTW->setRowCount(row+1);
  278     resTW->setItem(row, 0,
  279            new QTableWidgetItem(tr("Smallest document length (terms)")));
  280     resTW->setItem(row++, 1, new QTableWidgetItem(
  281                QString::number(res.mindoclen)));
  282 
  283     resTW->setRowCount(row+1);
  284     resTW->setItem(row, 0,
  285            new QTableWidgetItem(tr("Longest document length (terms)")));
  286     resTW->setItem(row++, 1, new QTableWidgetItem(
  287                QString::number(res.maxdoclen)));
  288 
  289     if (!theconfig)
  290     return;
  291 
  292     ConfSimple cs(theconfig->getIdxStatusFile().c_str(), 1);
  293     DbIxStatus st;
  294     cs.get("fn", st.fn);
  295     cs.get("docsdone", &st.docsdone);
  296     cs.get("filesdone", &st.filesdone);
  297     cs.get("fileerrors", &st.fileerrors);
  298     cs.get("dbtotdocs", &st.dbtotdocs);
  299     cs.get("totfiles", &st.totfiles);
  300 
  301     resTW->setRowCount(row+1);
  302     resTW->setItem(row, 0,
  303            new QTableWidgetItem(tr("Results from last indexing:")));
  304     resTW->setItem(row++, 1, new QTableWidgetItem(""));
  305     resTW->setRowCount(row+1);
  306     resTW->setItem(row, 0,
  307            new QTableWidgetItem(tr("  Documents created/updated")));
  308     resTW->setItem(row++, 1,
  309                    new QTableWidgetItem(QString::number(st.docsdone)));
  310     resTW->setRowCount(row+1);
  311     resTW->setItem(row, 0,
  312            new QTableWidgetItem(tr("  Files tested")));
  313     resTW->setItem(row++, 1,
  314                    new QTableWidgetItem(QString::number(st.filesdone)));
  315     resTW->setRowCount(row+1);
  316     resTW->setItem(row, 0,
  317            new QTableWidgetItem(tr("  Unindexed files")));
  318     resTW->setItem(row++, 1,
  319                    new QTableWidgetItem(QString::number(st.fileerrors)));
  320 
  321     baseWordLE->setText(QString::fromLocal8Bit(theconfig->getDbDir().c_str()));
  322 
  323     int64_t dbkbytes = fsTreeBytes(theconfig->getDbDir()) / 1024;
  324     if (dbkbytes < 0) {
  325     dbkbytes = 0;
  326     }
  327     resTW->setRowCount(row+1);
  328     resTW->setItem(row, 0,
  329            new QTableWidgetItem(tr("Database directory size")));
  330     resTW->setItem(row++, 1, new QTableWidgetItem(
  331                u8s2qs(displayableBytes(dbkbytes*1024))));
  332 
  333     vector<string> allmimetypes = theconfig->getAllMimeTypes();
  334     multimap<int, string> mtbycnt;
  335     for (vector<string>::const_iterator it = allmimetypes.begin();
  336      it != allmimetypes.end(); it++) {
  337     string reason;
  338     string q = string("mime:") + *it;
  339     Rcl::SearchData *sd = wasaStringToRcl(theconfig, "", q, reason);
  340     std::shared_ptr<Rcl::SearchData> rq(sd);
  341     Rcl::Query query(rcldb.get());
  342     if (!query.setQuery(rq)) {
  343         LOGERR("Query setup failed: "  << (query.getReason()) << "" );
  344         return;
  345     }
  346     int cnt = query.getResCnt();
  347     mtbycnt.insert(pair<int,string>(cnt,*it));
  348     }
  349     resTW->setRowCount(row+1);
  350     resTW->setItem(row, 0, new QTableWidgetItem(tr("MIME types:")));
  351     resTW->setItem(row++, 1, new QTableWidgetItem(""));
  352 
  353     for (multimap<int, string>::const_reverse_iterator it = mtbycnt.rbegin();
  354      it != mtbycnt.rend(); it++) {
  355     resTW->setRowCount(row+1);
  356     resTW->setItem(row, 0, new QTableWidgetItem(QString("    ") +
  357                                                     u8s2qs(it->second)));
  358     resTW->setItem(row++, 1, new QTableWidgetItem(
  359                QString::number(it->first)));
  360     }
  361 }
  362 
  363 void SpellW::showFailed()
  364 {
  365     statsLBL->setText("");
  366     int row = 0;
  367 
  368     Rcl::DbStats res;
  369     if (!rcldb->dbStats(res, true)) {
  370     LOGERR("SpellW::doExpand:rcldb::dbStats failed\n" );
  371     return;
  372     }
  373     for (auto entry : res.failedurls) {
  374     resTW->setRowCount(row+1);
  375     resTW->setItem(row, 0, new QTableWidgetItem(u8s2qs(entry)));
  376     resTW->setItem(row++, 1, new QTableWidgetItem(""));
  377     }
  378 }
  379 
  380 void SpellW::wordChanged(const QString &text)
  381 {
  382     if (text.isEmpty()) {
  383     expandPB->setEnabled(false);
  384         resTW->setRowCount(0);
  385     } else {
  386     expandPB->setEnabled(true);
  387     }
  388 }
  389 
  390 void SpellW::textDoubleClicked() {}
  391 void SpellW::textDoubleClicked(int row, int)
  392 {
  393     QTableWidgetItem *item = resTW->item(row, 0);
  394     if (item)
  395         emit(wordSelect(item->text()));
  396 }
  397 
  398 void SpellW::onModeChanged(int idx)
  399 {
  400     if (idx < 0 || idx > int(m_c2t.size()))
  401     return;
  402     comboboxchoice mode = m_c2t[idx];
  403     setModeCommon(mode);
  404 }
  405 
  406 void SpellW::setMode(comboboxchoice mode)
  407 {
  408     expTypeCMB->setCurrentIndex(cmbIdx(mode));
  409     setModeCommon(mode);
  410 }
  411 
  412 void SpellW::setModeCommon(comboboxchoice mode)
  413 {
  414     if (wordlessMode(m_prevmode)) {
  415         baseWordLE->setText("");
  416     }
  417     m_prevmode = mode;
  418     resTW->setRowCount(0);
  419     if (o_index_stripchars) {
  420     caseSensCB->setEnabled(false);
  421     diacSensCB->setEnabled(false);
  422     } else {
  423     caseSensCB->setEnabled(true);
  424     diacSensCB->setEnabled(true);
  425     }
  426    
  427     if (mode == TYPECMB_STEM) {
  428     stemLangCMB->setEnabled(true);
  429     diacSensCB->setChecked(false);
  430     diacSensCB->setEnabled(false);
  431     caseSensCB->setChecked(false);
  432     caseSensCB->setEnabled(false);
  433     } else {
  434     stemLangCMB->setEnabled(false);
  435     }
  436 
  437     if (wordlessMode(mode)) {
  438     baseWordLE->setEnabled(false);
  439     QStringList labels(tr("Item"));
  440     labels.push_back(tr("Value"));
  441     resTW->setHorizontalHeaderLabels(labels);
  442     diacSensCB->setEnabled(false);
  443     caseSensCB->setEnabled(false);
  444     doExpand();
  445     } else {
  446     baseWordLE->setEnabled(true);
  447     QStringList labels(tr("Term"));
  448     labels.push_back(tr("Doc. / Tot."));
  449     resTW->setHorizontalHeaderLabels(labels);
  450     prefs.termMatchType = mode;
  451     }
  452 }
  453 
  454 void SpellW::copy()
  455 {
  456   QItemSelectionModel * selection = resTW->selectionModel();
  457   QModelIndexList indexes = selection->selectedIndexes();
  458 
  459   if(indexes.size() < 1)
  460     return;
  461 
  462   // QModelIndex::operator < sorts first by row, then by column. 
  463   // this is what we need
  464   std::sort(indexes.begin(), indexes.end());
  465 
  466   // You need a pair of indexes to find the row changes
  467   QModelIndex previous = indexes.first();
  468   indexes.removeFirst();
  469   QString selected_text;
  470   QModelIndex current;
  471   Q_FOREACH(current, indexes)
  472   {
  473     QVariant data = resTW->model()->data(previous);
  474     QString text = data.toString();
  475     // At this point `text` contains the text in one cell
  476     selected_text.append(text);
  477     // If you are at the start of the row the row number of the previous index
  478     // isn't the same.  Text is followed by a row separator, which is a newline.
  479     if (current.row() != previous.row())
  480     {
  481       selected_text.append(QLatin1Char('\n'));
  482     }
  483     // Otherwise it's the same row, so append a column separator, which is a tab.
  484     else
  485     {
  486       selected_text.append(QLatin1Char('\t'));
  487     }
  488     previous = current;
  489   }
  490 
  491   // add last element
  492   selected_text.append(resTW->model()->data(current).toString());
  493   selected_text.append(QLatin1Char('\n'));
  494   qApp->clipboard()->setText(selected_text, QClipboard::Selection);
  495   qApp->clipboard()->setText(selected_text, QClipboard::Clipboard);
  496 }
  497 
  498 
  499 bool SpellW::eventFilter(QObject *target, QEvent *event)
  500 {
  501     if (event->type() != QEvent::KeyPress ||
  502     (target != resTW && target != resTW->viewport())) 
  503     return false;
  504 
  505     QKeyEvent *keyEvent = (QKeyEvent *)event;
  506     if(keyEvent->matches(QKeySequence::Copy) )
  507     {
  508     copy();
  509     return true;
  510     }
  511     return false;
  512 }
  513