1/* This file is part of Strigi Desktop Search
2 *
3 * Copyright (C) 2006 Jos van den Oever <jos@vandenoever.info>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public License
16 * along with this library; see the file COPYING.LIB. If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20#include "simplesearchgui.h"
21#include "dlgfilters.h"
22#include "dlglistindexedfiles.h"
23#include "searchtabs.h"
24#include "histogram.h"
25#include "searchview.h"
26#include <QtGui/QListWidget>
27#include <QtGui/QListWidgetItem>
28#include <QtGui/QLineEdit>
29#include <QtGui/QVBoxLayout>
30#include <QtGui/QStackedWidget>
31#include <QtGui/QLabel>
32#include <QtGui/QDockWidget>
33#include <QtGui/QPushButton>
34#include <QtGui/QToolButton>
35#include <QtGui/QFileDialog>
36#include <QtGui/QComboBox>
37#include <QtGui/QMenu>
38#include <QtGui/QMenuBar>
39#include <QtGui/QAction>
40#include <QtCore/QString>
41#include <QtCore/QDebug>
42#include <QtCore/QCoreApplication>
43#include <QtCore/QProcess>
44#include <QtCore/QTimer>
45#include <string>
46#include <map>
47#include <vector>
48using namespace std;
49
50SimpleSearchGui::SimpleSearchGui (QWidget * parent, Qt::WFlags flags)
51 : QMainWindow (parent, flags)
52{
53 mainview = new QStackedWidget();
54
55 QWidget* statuswidget = new QWidget();
56 QVBoxLayout *statuslayout = new QVBoxLayout;
57 statuslayout->setMargin(0);
58 statusview = new QLabel();
59 statusview->setAlignment(Qt::AlignTop);
60 statusview->setMargin(25);
61 indexing = false;
62 running = false;
63 starting = true;
64 indexeddirs = new QListWidget();
65 indexeddirs->setSelectionMode(QAbstractItemView::ExtendedSelection);
66 adddir = new QPushButton(tr("add directory"));
67 removedir = new QPushButton(tr("remove directory"));
68 toggleindexing = new QPushButton(tr("start indexing"));
69 toggledaemon = new QPushButton(tr("stop daemon"));
70 statuslayout->addWidget(statusview);
71 QHBoxLayout *hlayout = new QHBoxLayout;
72 hlayout->addWidget(toggledaemon);
73 hlayout->addWidget(toggleindexing);
74 statuslayout->addLayout(hlayout);
75
76 histogram = new Histogram();
77 histogram->setOrientation(Qt::Vertical);
78 fieldnames = new QComboBox();
79 fieldnames->addItems(strigi.getFieldNames());
80 fieldnames->setCurrentIndex(fieldnames->findText("system.last_modified_time"));
81 histogram->setFieldName("system.last_modified_time");
82 refreshHistogram = new QPushButton(tr("refresh histogram"));
83 refreshHistogram->setEnabled(false);
84
85/* vector<string> backends = ClientInterface::getBackEnds();
86 if (backends.size() > 1) {
87 backendsList = new QComboBox();
88 for (uint i = 0; i< backends.size(); ++i) {
89 QString backend = backends[i].c_str();
90 backendsList->insertItem(i, backend);
91 }
92 statuslayout->addWidget(backendsList);
93 } else {*/
94 backendsList = 0;
95// }
96 statuslayout->addWidget(indexeddirs);
97 hlayout = new QHBoxLayout;
98 hlayout->addWidget(adddir);
99 hlayout->addWidget(removedir);
100 statuslayout->addLayout(hlayout);
101 statuswidget->setLayout(statuslayout);
102
103 tabs = new SearchTabs();
104 tabs->addTab("kde", "kde");
105 tabs->addTab("msg", "content.mime_type:message/*");
106 tabs->addTab("irc", "system.location:*konversation*log");
107 tabs->addTab("mail", "content.mime_type:text/x-mail");
108 tabs->addTab("audio", "content.mime_type:audio/*");
109 tabs->addTab("other",
110 "-kde -system.location:*konversation*log -content.mime_type:message/* -content.mime_type:text/x-mail -content.mime_type:audio/*");
111 tabs->addTab("all", "");
112 mainview->addWidget(tabs);
113 mainview->addWidget(statuswidget);
114 asyncstrigi.updateStatus();
115 mainview->setCurrentIndex(1);
116
117 queryfield = new QLineEdit();
118 QVBoxLayout *layout = new QVBoxLayout;
119 layout->addWidget(mainview);
120 layout->addWidget(queryfield);
121
122/* The histogram widget is temporarily disabled
123 QVBoxLayout* histlayout = new QVBoxLayout();
124 QDockWidget* histwidget = new QDockWidget();
125 histwidget->setFeatures(QDockWidget::NoDockWidgetFeatures);
126 QWidget* histw = new QWidget();
127 histw->setLayout(histlayout);
128 histwidget->setWidget(histw);
129 histlayout->addWidget(fieldnames);
130 histlayout->addWidget(histogram);
131 histlayout->addWidget(refreshHistogram);
132 addDockWidget(Qt::LeftDockWidgetArea, histwidget);*/
133
134 centralview = new QWidget();
135 centralview->setLayout(layout);
136 setCentralWidget(centralview);
137
138 createActions();
139 createMenus();
140
141 connect(queryfield, SIGNAL(textChanged(const QString&)),
142 this, SLOT(query(const QString&)));
143 connect(toggleindexing, SIGNAL(clicked()),
144 this, SLOT(toggleIndexing()));
145 connect(toggledaemon, SIGNAL(clicked()),
146 this, SLOT(toggleDaemon()));
147 connect(adddir, SIGNAL(clicked()),
148 this, SLOT(addDirectory()));
149 connect(removedir, SIGNAL(clicked()),
150 this, SLOT(removeDirectory()));
151 queryfield->setFocus(Qt::ActiveWindowFocusReason);
152
153 connect(&asyncstrigi,SIGNAL(statusUpdated(const QMap<QString, QString>& )),
154 this, SLOT(updateStatus(const QMap<QString, QString>& )));
155// connect(tabs->getSearchView(), SIGNAL(gotHits(const QString&)),
156// histogram, SLOT(setQuery(const QString&)));
157 connect(refreshHistogram, SIGNAL(clicked()),
158 this, SLOT(refresh()));
159 connect(fieldnames, SIGNAL(currentIndexChanged(const QString&)),
160 histogram, SLOT(setFieldName(const QString&)));
161
162 QTimer *timer = new QTimer(this);
163 connect(timer, SIGNAL(timeout()), this, SLOT(updateStatus()));
164 timer->start(2000);
165 asyncstrigi.updateStatus();
166}
167void
168SimpleSearchGui::refresh() {
169 histogram->setQuery(queryfield->text());
170}
171void
172SimpleSearchGui::createMenus() {
173 fileMenu = menuBar()->addMenu(tr("&File"));
174 fileMenu->addAction(fileExitAct);
175
176 editMenu = menuBar()->addMenu(tr("&Edit"));
177 editMenu->addAction(editFiltersAct);
178 editMenu->addAction(editListIndexedFilesAct);
179}
180void
181SimpleSearchGui::createActions() {
182 fileExitAct = new QAction(tr("&Exit"), this);
183 fileExitAct->setShortcut(tr("Ctrl+Q"));
184 fileExitAct->setStatusTip(tr("Quit the program"));
185 connect(fileExitAct, SIGNAL(triggered()), this, SLOT(close()));
186
187 editFiltersAct = new QAction(tr("Edit Filters"), this);
188 editFiltersAct->setStatusTip(tr("Edit filename filters"));
189 connect(editFiltersAct, SIGNAL(triggered()), this, SLOT(editFilters()));
190
191 editListIndexedFilesAct = new QAction(tr("List indexed files"), this);
192 editListIndexedFilesAct->setStatusTip(tr("Show files indexed by strigi"));
193 connect(editListIndexedFilesAct, SIGNAL(triggered()), this, SLOT(editListIndexedFiles()));
194}
195void
196SimpleSearchGui::query(const QString& item) {
197 QString query = item.trimmed();
198 if (query.length() == 0) {
199 tabs->setQuery(QString());
200 asyncstrigi.updateStatus();
201 mainview->setCurrentIndex(1);
202 } else {
203 mainview->setCurrentIndex(0);
204 tabs->setQuery(query);
205 }
206 refreshHistogram->setEnabled(query.length());
207}
208void
209SimpleSearchGui::updateStatus() {
210 if (statusview->isVisible()) {
211 asyncstrigi.updateStatus();
212 }
213 if (fieldnames->count() == 0) {
214 fieldnames->addItems(strigi.getFieldNames());
215 fieldnames->setCurrentIndex(
216 fieldnames->findText("system.last_modified_time"));
217 }
218}
219void
220SimpleSearchGui::updateStatus(const QMap<QString, QString>& s) {
221// static bool first = true;
222 static bool attemptedstart = false;
223// if (!first && !statusview->isVisible()) return;
224// first = false;
225 QMap<QString, QString> status (s);
226 if (status.size() == 0) {
227 running = false;
228 editFiltersAct->setEnabled(false);
229 editListIndexedFilesAct->setEnabled(false);
230 status["Status"] = "Daemon is not running";
231 } else {
232 attemptedstart = true;
233 starting = false;
234 running = true;
235 editFiltersAct->setEnabled(true);
236 editListIndexedFilesAct->setEnabled(true);
237 if (indexeddirs->count() == 0) {
238 updateDirectories();
239 }
240 }
241 toggleindexing->setEnabled(running);
242 adddir->setEnabled(running);
243 removedir->setEnabled(running);
244 queryfield->setEnabled(running);
245 toggledaemon->setText((running)?tr("stop daemon"):tr("start daemon"));
246 toggledaemon->setEnabled(true);
247 if (backendsList) {
248 backendsList->setEnabled(!running);
249 }
250 bool idxng = status["Status"] == "indexing";
251 if (idxng != indexing) {
252 indexing = idxng;
253 toggleindexing->setText((indexing)?tr("stop indexing"):tr("start indexing"));
254 }
255
256 QMap<QString,QString>::const_iterator i;
257 QString text;
258 for (i = status.constBegin(); i != status.constEnd(); ++i) {
259 text += i.key();
260 text += ":\t";
261 text += i.value();
262 text += '\n';
263 }
264 statusview->setText(text);
265}
266void
267SimpleSearchGui::startDaemon() {
268 toggledaemon->setEnabled(false);
269 starting = true;
270 // try to start the daemon
271 QFileInfo exe = QCoreApplication::applicationDirPath()
272 + "/../../daemon/strigidaemon";
273 QStringList args;
274 if (backendsList) {
275 args += backendsList->currentText();
276 }
277 if (exe.exists()) {
278 // start not installed version
279 QProcess::startDetached(exe.absoluteFilePath(), args);
280 } else {
281 exe = QCoreApplication::applicationDirPath()+"/strigidaemon";
282 if (exe.exists()) {
283 QProcess::startDetached(exe.absoluteFilePath(), args);
284 } else {
285 // start installed version
286 QProcess::startDetached("strigidaemon");
287 }
288 }
289}
290void
291SimpleSearchGui::toggleDaemon() {
292 if (running) {
293 strigi.stopDaemon();
294 indexeddirs->clear();
295 } else {
296 startDaemon();
297 }
298}
299void
300SimpleSearchGui::toggleIndexing() {
301 if (indexing) {
302 strigi.stopIndexing();
303 } else {
304 strigi.startIndexing();
305 }
306}
307void
308SimpleSearchGui::addDirectory() {
309 // open file dialog
310 QString dir = QFileDialog::getExistingDirectory (this);
311 if (dir.size() <= 0) return;
312 for (int i=0; i<indexeddirs->count(); ++i) {
313 QString text = indexeddirs->item(i)->text();
314 if (dir.startsWith(text)) {
315 return;
316 }
317 if (text.startsWith(dir)) {
318 indexeddirs->takeItem(i);
319 i = 0;
320 }
321 }
322 indexeddirs->addItem(dir);
323 setDirectories();
324}
325void
326SimpleSearchGui::removeDirectory() {
327 QList<QListWidgetItem*> items = indexeddirs->selectedItems();
328
329 if (items.size() == 0)
330 return;
331
332 for (QList<QListWidgetItem*>::iterator iter = items.begin(); iter != items.end(); ++iter)
333 {
334 int row = indexeddirs->row (*iter);
335 indexeddirs->takeItem (row);
336 }
337 setDirectories();
338}
339void
340SimpleSearchGui::setDirectories() {
341 QStringList s;
342 for (int i=0; i<indexeddirs->count(); ++i) {
343 QString text = indexeddirs->item(i)->text();
344 s.append(text);
345 }
346 strigi.setIndexedDirectories(s);
347 updateDirectories();
348}
349void
350SimpleSearchGui::updateDirectories() {
351 indexeddirs->clear();
352 indexeddirs->addItems(strigi.getIndexedDirectories());
353}
354void
355SimpleSearchGui::editFilters() {
356 QList<QPair<bool,QString> > filters;
357 filters = strigi.getFilters();
358
359 DlgFilters dlg(filters);
360 if (dlg.exec() && filters != dlg.getFilters()) {
361 strigi.setFilters(dlg.getFilters());
362 }
363}
364void
365SimpleSearchGui::editListIndexedFiles() {
366 QStringList files = strigi.getIndexedFiles();
367 DlgListIndexedFiles dlg(files);
368 dlg.exec();
369}
370