"Fossies" - the Fresh Open Source Software Archive 
Member "recoll-1.26.3/qtgui/advsearch_w.cpp" (4 Sep 2019, 17631 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 "advsearch_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 "advsearch_w.h"
20
21 #include <qvariant.h>
22 #include <qpushbutton.h>
23 #include <qlabel.h>
24 #include <qlineedit.h>
25 #include <qframe.h>
26 #include <qcheckbox.h>
27 #include <qevent.h>
28 #include <qlayout.h>
29 #include <qtooltip.h>
30 #include <qwhatsthis.h>
31 #include <qmessagebox.h>
32 #include <QShortcut>
33
34 #include <string>
35 #include <map>
36 #include <algorithm>
37 using namespace std;
38
39 #include "recoll.h"
40 #include "rclconfig.h"
41 #include "log.h"
42 #include "searchdata.h"
43 #include "guiutils.h"
44 #include "rclhelp.h"
45
46 static const int initclausetypes[] = {1, 3, 0, 2, 5};
47 static const unsigned int iclausescnt = sizeof(initclausetypes) / sizeof(int);
48 static map<QString,QString> cat_translations;
49 static map<QString,QString> cat_rtranslations;
50
51 void AdvSearch::init()
52 {
53 (void)new HelpClient(this);
54 HelpClient::installMap((const char *)objectName().toUtf8(),
55 "RCL.SEARCH.GUI.COMPLEX");
56
57 // signals and slots connections
58 connect(delFiltypPB, SIGNAL(clicked()), this, SLOT(delFiltypPB_clicked()));
59 connect(searchPB, SIGNAL(clicked()), this, SLOT(runSearch()));
60 connect(filterDatesCB, SIGNAL(toggled(bool)),
61 this, SLOT(filterDatesCB_toggled(bool)));
62 connect(filterSizesCB, SIGNAL(toggled(bool)),
63 this, SLOT(filterSizesCB_toggled(bool)));
64 connect(restrictFtCB, SIGNAL(toggled(bool)),
65 this, SLOT(restrictFtCB_toggled(bool)));
66 connect(restrictCtCB, SIGNAL(toggled(bool)),
67 this, SLOT(restrictCtCB_toggled(bool)));
68 connect(dismissPB, SIGNAL(clicked()), this, SLOT(close()));
69 connect(browsePB, SIGNAL(clicked()), this, SLOT(browsePB_clicked()));
70 connect(addFiltypPB, SIGNAL(clicked()), this, SLOT(addFiltypPB_clicked()));
71
72 connect(delAFiltypPB, SIGNAL(clicked()),
73 this, SLOT(delAFiltypPB_clicked()));
74 connect(addAFiltypPB, SIGNAL(clicked()),
75 this, SLOT(addAFiltypPB_clicked()));
76 connect(saveFileTypesPB, SIGNAL(clicked()),
77 this, SLOT(saveFileTypes()));
78 connect(addClausePB, SIGNAL(clicked()), this, SLOT(addClause()));
79 connect(delClausePB, SIGNAL(clicked()), this, SLOT(delClause()));
80
81 new QShortcut(QKeySequence(Qt::Key_Up), this, SLOT(slotHistoryNext()));;
82 new QShortcut(QKeySequence(Qt::Key_Down), this, SLOT(slotHistoryPrev()));
83
84 conjunctCMB->insertItem(1, tr("All clauses"));
85 conjunctCMB->insertItem(2, tr("Any clause"));
86
87 // Create preconfigured clauses
88 for (unsigned int i = 0; i < iclausescnt; i++) {
89 addClause(initclausetypes[i], false);
90 }
91 // Tune initial state according to last saved
92 {
93 vector<SearchClauseW *>::iterator cit = m_clauseWins.begin();
94 unsigned int existing = m_clauseWins.size();
95 for (unsigned int i = 0; i < prefs.advSearchClauses.size(); i++) {
96 if (i < existing) {
97 (*cit)->tpChange(prefs.advSearchClauses[i]);
98 cit++;
99 } else {
100 addClause(prefs.advSearchClauses[i], false);
101 }
102 }
103 }
104 (*m_clauseWins.begin())->wordsLE->setFocus();
105
106 // Initialize min/max mtime from extrem values in the index
107 int minyear, maxyear;
108 if (rcldb) {
109 rcldb->maxYearSpan(&minyear, &maxyear);
110 minDateDTE->setDisplayFormat("yyyy-MM-dd");
111 maxDateDTE->setDisplayFormat("yyyy-MM-dd");
112 minDateDTE->setDate(QDate(minyear, 1, 1));
113 maxDateDTE->setDate(QDate(maxyear, 12, 31));
114 }
115
116 // Initialize lists of accepted and ignored mime types from config
117 // and settings
118 m_ignTypes = prefs.asearchIgnFilTyps;
119 m_ignByCats = prefs.fileTypesByCats;
120 restrictCtCB->setEnabled(false);
121 restrictCtCB->setChecked(m_ignByCats);
122 fillFileTypes();
123
124 subtreeCMB->insertItems(0, prefs.asearchSubdirHist);
125 subtreeCMB->setEditText("");
126
127 // The clauseline frame is needed to force designer to accept a
128 // vbox to englobe the base clauses grid and 'something else' (the
129 // vbox is so that we can then insert SearchClauseWs), but we
130 // don't want to see it.
131 clauseline->close();
132
133 bool calpop = 0;
134 minDateDTE->setCalendarPopup(calpop);
135 maxDateDTE->setCalendarPopup(calpop);
136
137 // Translations for known categories
138 cat_translations[QString::fromUtf8("texts")] = tr("text");
139 cat_rtranslations[tr("texts")] = QString::fromUtf8("text");
140
141 cat_translations[QString::fromUtf8("spreadsheet")] = tr("spreadsheet");
142 cat_rtranslations[tr("spreadsheets")] = QString::fromUtf8("spreadsheet");
143
144 cat_translations[QString::fromUtf8("presentation")] = tr("presentation");
145 cat_rtranslations[tr("presentation")] =QString::fromUtf8("presentation");
146
147 cat_translations[QString::fromUtf8("media")] = tr("media");
148 cat_rtranslations[tr("media")] = QString::fromUtf8("media");
149
150 cat_translations[QString::fromUtf8("message")] = tr("message");
151 cat_rtranslations[tr("message")] = QString::fromUtf8("message");
152
153 cat_translations[QString::fromUtf8("other")] = tr("other");
154 cat_rtranslations[tr("other")] = QString::fromUtf8("other");
155 }
156
157 void AdvSearch::saveCnf()
158 {
159 // Save my state
160 prefs.advSearchClauses.clear();
161 for (const auto& clause : m_clauseWins) {
162 prefs.advSearchClauses.push_back(clause->sTpCMB->currentIndex());
163 }
164 }
165
166 void AdvSearch::addClause(bool updsaved)
167 {
168 addClause(0, updsaved);
169 }
170
171 void AdvSearch::addClause(int tp, bool updsaved)
172 {
173 SearchClauseW *w = new SearchClauseW(clauseFRM);
174 m_clauseWins.push_back(w);
175 ((QVBoxLayout *)(clauseFRM->layout()))->addWidget(w);
176 w->show();
177 w->tpChange(tp);
178 if (m_clauseWins.size() > iclausescnt) {
179 delClausePB->setEnabled(true);
180 } else {
181 delClausePB->setEnabled(false);
182 }
183 if (updsaved) {
184 saveCnf();
185 }
186 }
187
188 void AdvSearch::delClause(bool updsaved)
189 {
190 if (m_clauseWins.size() <= iclausescnt)
191 return;
192 delete m_clauseWins.back();
193 m_clauseWins.pop_back();
194 if (m_clauseWins.size() > iclausescnt) {
195 delClausePB->setEnabled(true);
196 } else {
197 delClausePB->setEnabled(false);
198 }
199 if (updsaved) {
200 saveCnf();
201 }
202 }
203
204 void AdvSearch::delAFiltypPB_clicked()
205 {
206 yesFiltypsLB->selectAll();
207 delFiltypPB_clicked();
208 }
209
210 // Move selected file types from the searched to the ignored box
211 void AdvSearch::delFiltypPB_clicked()
212 {
213 QList<QListWidgetItem *> items = yesFiltypsLB->selectedItems();
214 for (QList<QListWidgetItem *>::iterator it = items.begin();
215 it != items.end(); it++) {
216 int row = yesFiltypsLB->row(*it);
217 QListWidgetItem *item = yesFiltypsLB->takeItem(row);
218 noFiltypsLB->insertItem(0, item);
219 }
220 guiListsToIgnTypes();
221 }
222
223 // Move selected file types from the ignored to the searched box
224 void AdvSearch::addFiltypPB_clicked()
225 {
226 QList<QListWidgetItem *> items = noFiltypsLB->selectedItems();
227 for (QList<QListWidgetItem *>::iterator it = items.begin();
228 it != items.end(); it++) {
229 int row = noFiltypsLB->row(*it);
230 QListWidgetItem *item = noFiltypsLB->takeItem(row);
231 yesFiltypsLB->insertItem(0, item);
232 }
233 guiListsToIgnTypes();
234 }
235
236 // Compute list of ignored mime type from widget lists
237 void AdvSearch::guiListsToIgnTypes()
238 {
239 yesFiltypsLB->sortItems();
240 noFiltypsLB->sortItems();
241 m_ignTypes.clear();
242 for (int i = 0; i < noFiltypsLB->count();i++) {
243 QListWidgetItem *item = noFiltypsLB->item(i);
244 m_ignTypes.append(item->text());
245 }
246 }
247 void AdvSearch::addAFiltypPB_clicked()
248 {
249 noFiltypsLB->selectAll();
250 addFiltypPB_clicked();
251 }
252
253 // Activate file type selection
254 void AdvSearch::restrictFtCB_toggled(bool on)
255 {
256 restrictCtCB->setEnabled(on);
257 yesFiltypsLB->setEnabled(on);
258 delFiltypPB->setEnabled(on);
259 addFiltypPB->setEnabled(on);
260 delAFiltypPB->setEnabled(on);
261 addAFiltypPB->setEnabled(on);
262 noFiltypsLB->setEnabled(on);
263 saveFileTypesPB->setEnabled(on);
264 }
265
266 // Activate file type selection
267 void AdvSearch::filterSizesCB_toggled(bool on)
268 {
269 minSizeLE->setEnabled(on);
270 maxSizeLE->setEnabled(on);
271 }
272 // Activate file type selection
273 void AdvSearch::filterDatesCB_toggled(bool on)
274 {
275 minDateDTE->setEnabled(on);
276 maxDateDTE->setEnabled(on);
277 }
278
279 void AdvSearch::restrictCtCB_toggled(bool on)
280 {
281 m_ignByCats = on;
282 // Only reset the list if we're enabled. Else this is init from prefs
283 if (restrictCtCB->isEnabled())
284 m_ignTypes.clear();
285 fillFileTypes();
286 }
287
288 void AdvSearch::fillFileTypes()
289 {
290 noFiltypsLB->clear();
291 yesFiltypsLB->clear();
292 noFiltypsLB->insertItems(0, m_ignTypes);
293
294 QStringList ql;
295 if (m_ignByCats == false) {
296 vector<string> types = theconfig->getAllMimeTypes();
297 rcldb->getAllDbMimeTypes(types);
298 sort(types.begin(), types.end());
299 types.erase(unique(types.begin(), types.end()), types.end());
300 for (vector<string>::iterator it = types.begin();
301 it != types.end(); it++) {
302 QString qs = QString::fromUtf8(it->c_str());
303 if (m_ignTypes.indexOf(qs) < 0)
304 ql.append(qs);
305 }
306 } else {
307 vector<string> cats;
308 theconfig->getMimeCategories(cats);
309 for (vector<string>::const_iterator it = cats.begin();
310 it != cats.end(); it++) {
311 map<QString, QString>::const_iterator it1;
312 QString cat;
313 if ((it1 = cat_translations.find(QString::fromUtf8(it->c_str())))
314 != cat_translations.end()) {
315 cat = it1->second;
316 } else {
317 cat = QString::fromUtf8(it->c_str());
318 }
319 if (m_ignTypes.indexOf(cat) < 0)
320 ql.append(cat);
321 }
322 }
323 yesFiltypsLB->insertItems(0, ql);
324 }
325
326 // Save current set of ignored file types to prefs
327 void AdvSearch::saveFileTypes()
328 {
329 prefs.asearchIgnFilTyps = m_ignTypes;
330 prefs.fileTypesByCats = m_ignByCats;
331 rwSettings(true);
332 }
333
334 void AdvSearch::browsePB_clicked()
335 {
336 QString dir = myGetFileName(true);
337 #ifdef _WIN32
338 string s = qs2utf8s(dir);
339 for (string::size_type i = 0; i < s.size(); i++) {
340 if (s[i] == '\\') {
341 s[i] = '/';
342 }
343 }
344 if (s.size() >= 2 && isalpha(s[0]) && s[1] == ':') {
345 s.erase(1,1);
346 s = string("/") + s;
347 }
348 dir = u8s2qs(s);
349 #endif
350 subtreeCMB->setEditText(dir);
351 }
352
353 size_t AdvSearch::stringToSize(QString qsize)
354 {
355 size_t size = size_t(-1);
356 qsize.replace(QRegExp("[\\s]+"), "");
357 if (!qsize.isEmpty()) {
358 string csize(qs2utf8s(qsize));
359 char *cp;
360 size = strtoll(csize.c_str(), &cp, 10);
361 if (*cp != 0) {
362 switch (*cp) {
363 case 'k': case 'K': size *= 1E3;break;
364 case 'm': case 'M': size *= 1E6;break;
365 case 'g': case 'G': size *= 1E9;break;
366 case 't': case 'T': size *= 1E12;break;
367 default:
368 QMessageBox::warning(0, "Recoll",
369 tr("Bad multiplier suffix in size filter"));
370 size = size_t(-1);
371 }
372 }
373 }
374 return size;
375 }
376
377 using namespace Rcl;
378 void AdvSearch::runSearch()
379 {
380 string stemLang = prefs.stemlang();
381 std::shared_ptr<SearchData> sdata(new SearchData(conjunctCMB->currentIndex() == 0 ?
382 SCLT_AND : SCLT_OR, stemLang));
383 bool hasclause = false;
384
385 for (vector<SearchClauseW*>::iterator it = m_clauseWins.begin();
386 it != m_clauseWins.end(); it++) {
387 SearchDataClause *cl;
388 if ((cl = (*it)->getClause())) {
389 sdata->addClause(cl);
390 hasclause = true;
391 }
392 }
393 if (!hasclause)
394 return;
395
396 if (restrictFtCB->isChecked() && noFiltypsLB->count() > 0) {
397 for (int i = 0; i < yesFiltypsLB->count(); i++) {
398 if (restrictCtCB->isChecked()) {
399 QString qcat = yesFiltypsLB->item(i)->text();
400 map<QString,QString>::const_iterator qit;
401 string cat;
402 if ((qit = cat_rtranslations.find(qcat)) !=
403 cat_rtranslations.end()) {
404 cat = qs2utf8s(qit->second);
405 } else {
406 cat = qs2utf8s(qcat);
407 }
408 vector<string> types;
409 theconfig->getMimeCatTypes(cat, types);
410 for (vector<string>::const_iterator it = types.begin();
411 it != types.end(); it++) {
412 sdata->addFiletype(*it);
413 }
414 } else {
415 sdata->addFiletype(qs2utf8s(yesFiltypsLB->item(i)->text()));
416 }
417 }
418 }
419
420 if (filterDatesCB->isChecked()) {
421 QDate mindate = minDateDTE->date();
422 QDate maxdate = maxDateDTE->date();
423 DateInterval di;
424 di.y1 = mindate.year();
425 di.m1 = mindate.month();
426 di.d1 = mindate.day();
427 di.y2 = maxdate.year();
428 di.m2 = maxdate.month();
429 di.d2 = maxdate.day();
430 sdata->setDateSpan(&di);
431 }
432 if (filterSizesCB->isChecked()) {
433 size_t size = stringToSize(minSizeLE->text());
434 sdata->setMinSize(size);
435 size = stringToSize(maxSizeLE->text());
436 sdata->setMaxSize(size);
437 }
438
439 if (!subtreeCMB->currentText().isEmpty()) {
440 QString current = subtreeCMB->currentText();
441
442 Rcl::SearchDataClausePath *pathclause =
443 new Rcl::SearchDataClausePath((const char*)current.toLocal8Bit(),
444 direxclCB->isChecked());
445 if (sdata->getTp() == SCLT_AND) {
446 sdata->addClause(pathclause);
447 } else {
448 std::shared_ptr<SearchData>
449 nsdata(new SearchData(SCLT_AND, stemLang));
450 nsdata->addClause(new Rcl::SearchDataClauseSub(sdata));
451 nsdata->addClause(pathclause);
452 sdata = nsdata;
453 }
454
455 // Keep history clean and sorted. Maybe there would be a
456 // simpler way to do this
457 list<QString> entries;
458 for (int i = 0; i < subtreeCMB->count(); i++) {
459 entries.push_back(subtreeCMB->itemText(i));
460 }
461 entries.push_back(subtreeCMB->currentText());
462 entries.sort();
463 entries.unique();
464 LOGDEB("Subtree list now has " << (entries.size()) << " entries\n" );
465 subtreeCMB->clear();
466 for (list<QString>::iterator it = entries.begin();
467 it != entries.end(); it++) {
468 subtreeCMB->addItem(*it);
469 }
470 subtreeCMB->setCurrentIndex(subtreeCMB->findText(current));
471 prefs.asearchSubdirHist.clear();
472 for (int index = 0; index < subtreeCMB->count(); index++)
473 prefs.asearchSubdirHist.push_back(subtreeCMB->itemText(index));
474 }
475 saveCnf();
476 g_advshistory && g_advshistory->push(sdata);
477 emit setDescription("");
478 emit startSearch(sdata, false);
479 }
480
481
482 // Set up fields from existing search data, which must be compatible
483 // with what we can do...
484 void AdvSearch::fromSearch(std::shared_ptr<SearchData> sdata)
485 {
486 if (sdata->m_tp == SCLT_OR)
487 conjunctCMB->setCurrentIndex(1);
488 else
489 conjunctCMB->setCurrentIndex(0);
490
491 while (sdata->m_query.size() > m_clauseWins.size()) {
492 addClause();
493 }
494
495 subtreeCMB->setEditText("");
496 direxclCB->setChecked(0);
497
498 for (unsigned int i = 0; i < sdata->m_query.size(); i++) {
499 // Set fields from clause
500 if (sdata->m_query[i]->getTp() == SCLT_SUB) {
501 LOGERR("AdvSearch::fromSearch: SUB clause found !\n" );
502 continue;
503 }
504 if (sdata->m_query[i]->getTp() == SCLT_PATH) {
505 SearchDataClausePath *cs =
506 dynamic_cast<SearchDataClausePath*>(sdata->m_query[i]);
507 // We can only use one such clause. There should be only one too
508 // if this is sfrom aved search data.
509 QString qdir = QString::fromLocal8Bit(cs->gettext().c_str());
510 subtreeCMB->setEditText(qdir);
511 direxclCB->setChecked(cs->getexclude());
512 continue;
513 }
514 SearchDataClauseSimple *cs =
515 dynamic_cast<SearchDataClauseSimple*>(sdata->m_query[i]);
516 m_clauseWins[i]->setFromClause(cs);
517 }
518 for (unsigned int i = sdata->m_query.size(); i < m_clauseWins.size(); i++) {
519 m_clauseWins[i]->clear();
520 }
521
522 restrictCtCB->setChecked(0);
523 if (!sdata->m_filetypes.empty()) {
524 restrictFtCB_toggled(1);
525 delAFiltypPB_clicked();
526 for (unsigned int i = 0; i < sdata->m_filetypes.size(); i++) {
527 QString ft = QString::fromUtf8(sdata->m_filetypes[i].c_str());
528 QList<QListWidgetItem *> lst =
529 noFiltypsLB->findItems(ft, Qt::MatchExactly);
530 if (!lst.isEmpty()) {
531 int row = noFiltypsLB->row(lst[0]);
532 QListWidgetItem *item = noFiltypsLB->takeItem(row);
533 yesFiltypsLB->insertItem(0, item);
534 }
535 }
536 yesFiltypsLB->sortItems();
537 } else {
538 addAFiltypPB_clicked();
539 restrictFtCB_toggled(0);
540 }
541
542 if (sdata->m_haveDates) {
543 filterDatesCB->setChecked(1);
544 DateInterval &di(sdata->m_dates);
545 QDate mindate(di.y1, di.m1, di.d1);
546 QDate maxdate(di.y2, di.m2, di.d2);
547 minDateDTE->setDate(mindate);
548 maxDateDTE->setDate(maxdate);
549 } else {
550 filterDatesCB->setChecked(0);
551 QDate date;
552 minDateDTE->setDate(date);
553 maxDateDTE->setDate(date);
554 }
555
556 if (sdata->m_maxSize != (size_t)-1 || sdata->m_minSize != (size_t)-1) {
557 filterSizesCB->setChecked(1);
558 QString sz;
559 if (sdata->m_minSize != (size_t)-1) {
560 sz.setNum(sdata->m_minSize);
561 minSizeLE->setText(sz);
562 } else {
563 minSizeLE->setText("");
564 }
565 if (sdata->m_maxSize != (size_t)-1) {
566 sz.setNum(sdata->m_maxSize);
567 maxSizeLE->setText(sz);
568 } else {
569 maxSizeLE->setText("");
570 }
571 } else {
572 filterSizesCB->setChecked(0);
573 minSizeLE->setText("");
574 maxSizeLE->setText("");
575 }
576 }
577
578 void AdvSearch::slotHistoryNext()
579 {
580 if (g_advshistory == 0)
581 return;
582 std::shared_ptr<Rcl::SearchData> sd = g_advshistory->getnewer();
583 if (!sd)
584 return;
585 fromSearch(sd);
586 }
587
588 void AdvSearch::slotHistoryPrev()
589 {
590 if (g_advshistory == 0)
591 return;
592 std::shared_ptr<Rcl::SearchData> sd = g_advshistory->getolder();
593 if (!sd)
594 return;
595 fromSearch(sd);
596 }
597
598