Switch to side-by-side view

--- a/src/qtgui/rclmain_w.cpp
+++ b/src/qtgui/rclmain_w.cpp
@@ -48,6 +48,7 @@
 #include <qapplication.h>
 #include <qcursor.h>
 #include <qevent.h>
+#include <QFileSystemWatcher>
 
 #include "recoll.h"
 #include "debuglog.h"
@@ -73,6 +74,7 @@
 #include "idxsched.h"
 #include "crontool.h"
 #include "rtitool.h"
+#include "indexer.h"
 
 using namespace confgui;
 
@@ -97,7 +99,8 @@
 				(const char *)tr("filtered").toUtf8());
 
     periodictimer = new QTimer(this);
-
+    m_watcher.addPath(QString::fromLocal8Bit(
+			  theconfig->getIdxStatusFile().c_str()));
     // At least some versions of qt4 don't display the status bar if
     // it's not created here.
     (void)statusBar();
@@ -203,6 +206,8 @@
     connect(sc, SIGNAL (activated()), 
 	    this, SLOT (focusToSearch()));
 
+    connect(&m_watcher, SIGNAL(fileChanged(QString)), 
+	    this, SLOT(idxStatus()));
     connect(sSearch, SIGNAL(startSearch(RefCntr<Rcl::SearchData>)), 
 	    this, SLOT(startSearch(RefCntr<Rcl::SearchData>)));
     connect(sSearch, SIGNAL(clearSearch()), 
@@ -473,82 +478,117 @@
     // We'd prefer to do this in the exit handler, but it's apparently to late
     // and some of the qt stuff is already dead at this point?
     LOGDEB2(("RclMain::fileExit() : stopping idx thread\n"));
-    stop_idxthread();
+
+    // Do we want to stop an ongoing index operation here ? 
+    // I guess not. We did use to cancel the indexing thread.
+
     // Let the exit handler clean up the rest (internal recoll stuff).
     exit(0);
 }
 
-// This is called by a periodic timer to check the status of the
-// indexing thread and a possible need to exit
+void RclMain::idxStatus()
+{
+    ConfSimple cs(theconfig->getIdxStatusFile().c_str(), 1);
+    QString msg = tr("Indexing in progress: ");
+    DbIxStatus status;
+    string val;
+    cs.get("phase", val);
+    status.phase = DbIxStatus::Phase(atoi(val.c_str()));
+    cs.get("fn", status.fn);
+    cs.get("docsdone", val);
+    status.docsdone = atoi(val.c_str());
+    cs.get("filesdone", val);
+    status.filesdone = atoi(val.c_str());
+    cs.get("dbtotdocs", val);
+    status.dbtotdocs = atoi(val.c_str());
+
+    QString phs;
+    switch (status.phase) {
+    case DbIxStatus::DBIXS_NONE:phs=tr("None");break;
+    case DbIxStatus::DBIXS_FILES: phs=tr("Updating");break;
+    case DbIxStatus::DBIXS_PURGE: phs=tr("Purge");break;
+    case DbIxStatus::DBIXS_STEMDB: phs=tr("Stemdb");break;
+    case DbIxStatus::DBIXS_CLOSING:phs=tr("Closing");break;
+    case DbIxStatus::DBIXS_DONE:phs=tr("Done");break;
+    case DbIxStatus::DBIXS_MONITOR:phs=tr("Monitor");break;
+    default: phs=tr("Unknown");break;
+    }
+    msg += phs + " ";
+    if (status.phase == DbIxStatus::DBIXS_FILES) {
+	char cnts[100];
+	if (status.dbtotdocs > 0)
+	    sprintf(cnts,"(%d/%d/%d) ", status.docsdone, status.filesdone, 
+		    status.dbtotdocs);
+	else
+	    sprintf(cnts, "(%d/%d) ", status.docsdone, status.filesdone);
+	msg += QString::fromAscii(cnts) + " ";
+    }
+    string mf;int ecnt = 0;
+    string fcharset = theconfig->getDefCharset(true);
+    if (!transcode(status.fn, mf, fcharset, "UTF-8", &ecnt) || ecnt) {
+	mf = url_encode(status.fn, 0);
+    }
+    msg += QString::fromUtf8(mf.c_str());
+    statusBar()->showMessage(msg, 4000);
+}
+
+// This is called by a periodic timer to check the status of 
+// indexing, a possible need to exit, and cleanup exited viewers
 void RclMain::periodic100()
 {
-    // Check if indexing thread done
-    if (idxthread_getStatus() != IDXTS_NULL) {
-	// Indexing is stopped
-	fileToggleIndexingAction->setText(tr("Update &Index"));
-	fileToggleIndexingAction->setEnabled(TRUE);
-	if (m_idxStatusAck == false) {
-	    m_idxStatusAck = true;
-	    if (idxthread_getStatus() != IDXTS_OK) {
-		if (idxthread_idxInterrupted()) {
-		    QMessageBox::warning(0, "Recoll", 
-					 tr("Indexing interrupted"));
-		} else {
-		    QMessageBox::warning(0, "Recoll", 
-		       QString::fromAscii(idxthread_getReason().c_str()));
-		}
+    LOGDEB2(("Periodic100\n"));
+    if (m_idxproc) {
+	// An indexing process was launched. If its' done, see status.
+	int status;
+	bool exited = m_idxproc->maybereap(&status);
+	if (exited) {
+	    deleteZ(m_idxproc);
+	    if (status) {
+		QMessageBox::warning(0, "Recoll", 
+				     tr("Indexing failed"));
 	    }
-	    // Make sure we reopen the db to get the results. If there
-	    // is current search data, we should reset it else things
-	    // are inconsistent (ie: applying sort will fail. But we
-	    // don't like erasing results while the user may be
-	    // looking at them either). Fixing this would be
-	    // relatively complicated (keep an open/close gen number
-	    // and check this / restart query in DocSeqDb() ?)
 	    string reason;
 	    maybeOpenDb(reason, 1);
-            periodictimer->setInterval(1000);
+	} else {
+	    // update/show status even if the status file did not
+	    // change (else the status line goes blank during
+	    // lengthy operations).
+	    idxStatus();
 	}
-    } else {
-	// Indexing is running
-	m_idxStatusAck = false;
+    }
+    // Update the "start/stop indexing" menu entry, can't be done from
+    // the "start/stop indexing" slot itself
+    if (m_idxproc) {
 	fileToggleIndexingAction->setText(tr("Stop &Indexing"));
 	fileToggleIndexingAction->setEnabled(TRUE);
-        periodictimer->setInterval(100);
-	// The toggle thing is for the status to flash
-	if (m_periodicToggle < 9) {
-	    QString msg = tr("Indexing in progress: ");
-	    DbIxStatus status = idxthread_idxStatus();
-	    QString phs;
-	    switch (status.phase) {
-	    case DbIxStatus::DBIXS_FILES: phs=tr("Files");break;
-	    case DbIxStatus::DBIXS_PURGE: phs=tr("Purge");break;
-	    case DbIxStatus::DBIXS_STEMDB: phs=tr("Stemdb");break;
-	    case DbIxStatus::DBIXS_CLOSING:phs=tr("Closing");break;
-	    default: phs=tr("Unknown");break;
-	    }
-	    msg += phs + " ";
-	    if (status.phase == DbIxStatus::DBIXS_FILES) {
-		char cnts[100];
-		if (status.dbtotdocs>0)
-		    sprintf(cnts,"(%d/%d) ",status.docsdone, status.dbtotdocs);
-		else
-		    sprintf(cnts, "(%d) ", status.docsdone);
-		msg += QString::fromAscii(cnts) + " ";
-	    }
-	    string mf;int ecnt = 0;
-	    string fcharset = theconfig->getDefCharset(true);
-	    if (!transcode(status.fn, mf, fcharset, "UTF-8", &ecnt) || ecnt) {
-		mf = url_encode(status.fn, 0);
-	    }
-	    msg += QString::fromUtf8(mf.c_str());
-	    statusBar()->showMessage(msg, 4000);
-	} else if (m_periodicToggle == 9) {
-	    statusBar()->showMessage("");
+    } else {
+	fileToggleIndexingAction->setText(tr("Update &Index"));
+	// No indexer of our own runnin, but the real time one may be up, check
+	// for some other indexer.
+	Pidfile pidfile(theconfig->getPidfile());
+	if (pidfile.open() == 0) {
+	    fileToggleIndexingAction->setEnabled(TRUE);
+	} else {
+	    fileToggleIndexingAction->setEnabled(FALSE);
+	}	    
+    }
+
+    // Possibly cleanup the dead viewers
+    for (vector<ExecCmd*>::iterator it = m_viewers.begin();
+	 it != m_viewers.end(); it++) {
+	int status;
+	if ((*it)->maybereap(&status)) {
+	    deleteZ(*it);
 	}
-	if (++m_periodicToggle >= 10)
-	    m_periodicToggle = 0;
-    }
+    }
+    vector<ExecCmd*> v;
+    for (vector<ExecCmd*>::iterator it = m_viewers.begin();
+	 it != m_viewers.end(); it++) {
+	if (*it)
+	    v.push_back(*it);
+    }
+    m_viewers = v;
+
     if (recollNeedsExit)
 	fileExit();
 }
@@ -558,14 +598,16 @@
 // re-enabled by the indexing status check
 void RclMain::toggleIndexing()
 {
-    if (idxthread_getStatus() == IDXTS_NULL) {
-	// Indexing was in progress, stop it
-	stop_indexing();
-        periodictimer->setInterval(1000);
-	fileToggleIndexingAction->setText(tr("Update &Index"));
-    } else {
-	start_indexing(false);
-        periodictimer->setInterval(100);
+    if (m_idxproc) {
+	// Indexing was in progress, request stop. Let the periodic
+	// routine check for the results.
+	kill(m_idxproc->getChildPid(), SIGTERM);
+    } else {
+	list<string> args;
+	args.push_back("-c");
+	args.push_back(theconfig->getConfDir());
+	m_idxproc = new ExecCmd;
+	m_idxproc->startExec("recollindex", args, false, false);
 	fileToggleIndexingAction->setText(tr("Stop &Indexing"));
     }
     fileToggleIndexingAction->setEnabled(FALSE);
@@ -574,15 +616,14 @@
 // Start a db query and set the reslist docsource
 void RclMain::startSearch(RefCntr<Rcl::SearchData> sdata)
 {
-    LOGDEB(("RclMain::startSearch. Indexing %s\n", 
-	    idxthread_getStatus() == IDXTS_NULL?"on":"off"));
+    LOGDEB(("RclMain::startSearch. Indexing %s\n", m_idxproc?"on":"off"));
     emit searchReset();
     m_source = RefCntr<DocSequence>();
 
     // The db may have been closed at the end of indexing
     string reason;
     // If indexing is being performed, we reopen the db at each query.
-    if (!maybeOpenDb(reason, idxthread_getStatus() == IDXTS_NULL)) {
+    if (!maybeOpenDb(reason, m_idxproc != 0)) {
 	QMessageBox::critical(0, "Recoll", QString(reason.c_str()));
 	return;
     }
@@ -970,7 +1011,7 @@
 		    QMessageBox::warning(0, tr("Warning"), 
 				       tr("Index not up to date for this file. "
 					"Refusing to risk showing the wrong "
-					"data. Click ok to update the "
+					"entry. Click Ok to update the "
 					"index for this file, then re-run the "
 					"query when indexing is done. "
 					"Else, Cancel."),
@@ -980,7 +1021,8 @@
 		if (rep == QMessageBox::Ok) {
 		    LOGDEB(("Requesting index update for %s\n", 
 			    doc.url.c_str()));
-		    start_indexing(false, vector<Rcl::Doc>(1, doc));
+		    vector<Rcl::Doc> docs(1, doc);
+		    updateIdxForDocs(docs);
 		}
 		return;
 	    }
@@ -1023,6 +1065,30 @@
     curPreview->makeDocCurrent(doc, docnum);
 }
 
+void RclMain::updateIdxForDocs(vector<Rcl::Doc>& docs)
+{
+    if (m_idxproc) {
+	QMessageBox::warning(0, tr("Warning"), 
+			     tr("Can't update index: indexer running"),
+			     QMessageBox::Ok, 
+			     QMessageBox::NoButton);
+	return;
+    }
+	
+    vector<string> paths;
+    if (ConfIndexer::docsToPaths(docs, paths)) {
+	list<string> args;
+	args.push_back("-c");
+	args.push_back(theconfig->getConfDir());
+	args.push_back("-i");
+	args.insert(args.end(), paths.begin(), paths.end());
+	m_idxproc = new ExecCmd;
+	m_idxproc->startExec("recollindex", args, false, false);
+	fileToggleIndexingAction->setText(tr("Stop &Indexing"));
+    }
+    fileToggleIndexingAction->setEnabled(FALSE);
+}
+
 /** 
  * Open a preview window for a given document, no linking to result list
  *