Split monster file rclmain_w.cpp. No code changes
This commit is contained in:
parent
12682b7ab2
commit
64b0c9ca32
300
src/qtgui/rclm_idx.cpp
Normal file
300
src/qtgui/rclm_idx.cpp
Normal file
@ -0,0 +1,300 @@
|
|||||||
|
/* Copyright (C) 2005 J.F.Dockes
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the
|
||||||
|
* Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
#include "autoconfig.h"
|
||||||
|
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include "execmd.h"
|
||||||
|
#include "debuglog.h"
|
||||||
|
#include "transcode.h"
|
||||||
|
#include "indexer.h"
|
||||||
|
#include "rclmain_w.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
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::fromUtf8(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()
|
||||||
|
{
|
||||||
|
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) {
|
||||||
|
if (m_idxkilled) {
|
||||||
|
QMessageBox::warning(0, "Recoll",
|
||||||
|
tr("Indexing interrupted"));
|
||||||
|
m_idxkilled = false;
|
||||||
|
} else {
|
||||||
|
QMessageBox::warning(0, "Recoll",
|
||||||
|
tr("Indexing failed"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// On the first run, show missing helpers. We only do this once
|
||||||
|
if (m_firstIndexing)
|
||||||
|
showMissingHelpers();
|
||||||
|
}
|
||||||
|
string reason;
|
||||||
|
maybeOpenDb(reason, 1);
|
||||||
|
} else {
|
||||||
|
// update/show status even if the status file did not
|
||||||
|
// change (else the status line goes blank during
|
||||||
|
// lengthy operations).
|
||||||
|
idxStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Update the "start/stop indexing" menu entry, can't be done from
|
||||||
|
// the "start/stop indexing" slot itself
|
||||||
|
IndexerState prevstate = m_indexerState;
|
||||||
|
if (m_idxproc) {
|
||||||
|
m_indexerState = IXST_RUNNINGMINE;
|
||||||
|
fileToggleIndexingAction->setText(tr("Stop &Indexing"));
|
||||||
|
fileToggleIndexingAction->setEnabled(true);
|
||||||
|
fileRetryFailedAction->setEnabled(false);
|
||||||
|
fileRebuildIndexAction->setEnabled(false);
|
||||||
|
periodictimer->setInterval(200);
|
||||||
|
} else {
|
||||||
|
Pidfile pidfile(theconfig->getPidfile());
|
||||||
|
if (pidfile.open() == 0) {
|
||||||
|
m_indexerState = IXST_NOTRUNNING;
|
||||||
|
fileToggleIndexingAction->setText(tr("Update &Index"));
|
||||||
|
fileRetryFailedAction->setEnabled(true);
|
||||||
|
fileToggleIndexingAction->setEnabled(true);
|
||||||
|
fileRebuildIndexAction->setEnabled(true);
|
||||||
|
periodictimer->setInterval(1000);
|
||||||
|
} else {
|
||||||
|
// Real time or externally started batch indexer running
|
||||||
|
m_indexerState = IXST_RUNNINGNOTMINE;
|
||||||
|
fileToggleIndexingAction->setText(tr("Stop &Indexing"));
|
||||||
|
fileToggleIndexingAction->setEnabled(true);
|
||||||
|
fileRetryFailedAction->setEnabled(false);
|
||||||
|
fileRebuildIndexAction->setEnabled(false);
|
||||||
|
periodictimer->setInterval(200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((prevstate == IXST_RUNNINGMINE || prevstate == IXST_RUNNINGNOTMINE)
|
||||||
|
&& m_indexerState == IXST_NOTRUNNING) {
|
||||||
|
showTrayMessage("Indexing done");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This gets called when the "update index" action is activated. It executes
|
||||||
|
// the requested action, and disables the menu entry. This will be
|
||||||
|
// re-enabled by the indexing status check
|
||||||
|
void RclMain::toggleIndexing()
|
||||||
|
{
|
||||||
|
switch (m_indexerState) {
|
||||||
|
case IXST_RUNNINGMINE:
|
||||||
|
if (m_idxproc) {
|
||||||
|
// Indexing was in progress, request stop. Let the periodic
|
||||||
|
// routine check for the results.
|
||||||
|
int pid = m_idxproc->getChildPid();
|
||||||
|
if (pid > 0) {
|
||||||
|
kill(pid, SIGTERM);
|
||||||
|
m_idxkilled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case IXST_RUNNINGNOTMINE:
|
||||||
|
{
|
||||||
|
int rep =
|
||||||
|
QMessageBox::information(0, tr("Warning"),
|
||||||
|
tr("The current indexing process "
|
||||||
|
"was not started from this "
|
||||||
|
"interface. Click Ok to kill it "
|
||||||
|
"anyway, or Cancel to leave it alone"),
|
||||||
|
QMessageBox::Ok,
|
||||||
|
QMessageBox::Cancel,
|
||||||
|
QMessageBox::NoButton);
|
||||||
|
if (rep == QMessageBox::Ok) {
|
||||||
|
Pidfile pidfile(theconfig->getPidfile());
|
||||||
|
pid_t pid = pidfile.open();
|
||||||
|
if (pid > 0)
|
||||||
|
kill(pid, SIGTERM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case IXST_NOTRUNNING:
|
||||||
|
{
|
||||||
|
// Could also mean that no helpers are missing, but then we
|
||||||
|
// won't try to show a message anyway (which is what
|
||||||
|
// firstIndexing is used for)
|
||||||
|
string mhd;
|
||||||
|
m_firstIndexing = !theconfig->getMissingHelperDesc(mhd);
|
||||||
|
|
||||||
|
vector<string> args;
|
||||||
|
|
||||||
|
string badpaths;
|
||||||
|
args.push_back("recollindex");
|
||||||
|
args.push_back("-E");
|
||||||
|
ExecCmd::backtick(args, badpaths);
|
||||||
|
if (!badpaths.empty()) {
|
||||||
|
int rep =
|
||||||
|
QMessageBox::warning(0, tr("Bad paths"),
|
||||||
|
tr("Bad paths in configuration file:\n") +
|
||||||
|
QString::fromLocal8Bit(badpaths.c_str()),
|
||||||
|
QMessageBox::Ok,
|
||||||
|
QMessageBox::Cancel,
|
||||||
|
QMessageBox::NoButton);
|
||||||
|
if (rep == QMessageBox::Cancel)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
args.clear();
|
||||||
|
args.push_back("-c");
|
||||||
|
args.push_back(theconfig->getConfDir());
|
||||||
|
if (fileRetryFailedAction->isChecked())
|
||||||
|
args.push_back("-k");
|
||||||
|
m_idxproc = new ExecCmd;
|
||||||
|
m_idxproc->startExec("recollindex", args, false, false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case IXST_UNKNOWN:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RclMain::rebuildIndex()
|
||||||
|
{
|
||||||
|
switch (m_indexerState) {
|
||||||
|
case IXST_UNKNOWN:
|
||||||
|
case IXST_RUNNINGMINE:
|
||||||
|
case IXST_RUNNINGNOTMINE:
|
||||||
|
return; //?? Should not have been called
|
||||||
|
case IXST_NOTRUNNING:
|
||||||
|
{
|
||||||
|
int rep =
|
||||||
|
QMessageBox::warning(0, tr("Erasing index"),
|
||||||
|
tr("Reset the index and start "
|
||||||
|
"from scratch ?"),
|
||||||
|
QMessageBox::Ok,
|
||||||
|
QMessageBox::Cancel,
|
||||||
|
QMessageBox::NoButton);
|
||||||
|
if (rep == QMessageBox::Ok) {
|
||||||
|
// Could also mean that no helpers are missing, but then we
|
||||||
|
// won't try to show a message anyway (which is what
|
||||||
|
// firstIndexing is used for)
|
||||||
|
string mhd;
|
||||||
|
m_firstIndexing = !theconfig->getMissingHelperDesc(mhd);
|
||||||
|
vector<string> args;
|
||||||
|
args.push_back("-c");
|
||||||
|
args.push_back(theconfig->getConfDir());
|
||||||
|
args.push_back("-z");
|
||||||
|
m_idxproc = new ExecCmd;
|
||||||
|
m_idxproc->startExec("recollindex", args, false, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)) {
|
||||||
|
vector<string> args;
|
||||||
|
args.push_back("-c");
|
||||||
|
args.push_back(theconfig->getConfDir());
|
||||||
|
args.push_back("-e");
|
||||||
|
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);
|
||||||
|
fileRetryFailedAction->setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
259
src/qtgui/rclm_preview.cpp
Normal file
259
src/qtgui/rclm_preview.cpp
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
/* Copyright (C) 2005 J.F.Dockes
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the
|
||||||
|
* Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
#include "autoconfig.h"
|
||||||
|
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QShortcut>
|
||||||
|
|
||||||
|
#include "debuglog.h"
|
||||||
|
#include "internfile.h"
|
||||||
|
#include "rclzg.h"
|
||||||
|
#include "rclmain_w.h"
|
||||||
|
|
||||||
|
static const QKeySequence quitKeySeq("Ctrl+q");
|
||||||
|
|
||||||
|
// If a preview (toplevel) window gets closed by the user, we need to
|
||||||
|
// clean up because there is no way to reopen it. And check the case
|
||||||
|
// where the current one is closed
|
||||||
|
void RclMain::previewClosed(Preview *w)
|
||||||
|
{
|
||||||
|
LOGDEB(("RclMain::previewClosed(%p)\n", w));
|
||||||
|
if (w == curPreview) {
|
||||||
|
LOGDEB(("Active preview closed\n"));
|
||||||
|
curPreview = 0;
|
||||||
|
} else {
|
||||||
|
LOGDEB(("Old preview closed\n"));
|
||||||
|
}
|
||||||
|
delete w;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Document up to date check. The main problem we try to solve is
|
||||||
|
// displaying the wrong message from a compacted mail folder.
|
||||||
|
//
|
||||||
|
// Also we should re-run the query after updating the index because
|
||||||
|
// the ipaths may be wrong in the current result list. For now, the
|
||||||
|
// user does this by clicking search again once the indexing is done
|
||||||
|
//
|
||||||
|
// We only do this for the main index, else jump and prey (cant update
|
||||||
|
// anyway, even the makesig() call might not make sense for our base
|
||||||
|
// config)
|
||||||
|
bool RclMain::containerUpToDate(Rcl::Doc& doc)
|
||||||
|
{
|
||||||
|
// If ipath is empty, we decide we don't care. Also, we need an index,
|
||||||
|
if (doc.ipath.empty() || rcldb == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
string udi;
|
||||||
|
doc.getmeta(Rcl::Doc::keyudi, &udi);
|
||||||
|
if (udi.empty()) {
|
||||||
|
// Whatever...
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
string sig;
|
||||||
|
if (!FileInterner::makesig(theconfig, doc, sig)) {
|
||||||
|
QMessageBox::warning(0, "Recoll", tr("Can't access file: ") +
|
||||||
|
QString::fromLocal8Bit(doc.url.c_str()));
|
||||||
|
// Let's try the preview anyway...
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rcldb->needUpdate(udi, sig)) {
|
||||||
|
// Alles ist in ordnung
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can only run indexing on the main index (dbidx 0)
|
||||||
|
bool ismainidx = rcldb->whatDbIdx(doc) == 0;
|
||||||
|
// Indexer already running?
|
||||||
|
bool ixnotact = (m_indexerState == IXST_NOTRUNNING);
|
||||||
|
|
||||||
|
QString msg = tr("Index not up to date for this file. "
|
||||||
|
"Refusing to risk showing the wrong entry. ");
|
||||||
|
if (ixnotact && ismainidx) {
|
||||||
|
msg += tr("Click Ok to update the "
|
||||||
|
"index for this file, then you will need to "
|
||||||
|
"re-run the query when indexing is done. ");
|
||||||
|
} else if (ismainidx) {
|
||||||
|
msg += tr("The indexer is running so things should "
|
||||||
|
"improve when it's done. ");
|
||||||
|
} else if (ixnotact) {
|
||||||
|
// Not main index
|
||||||
|
msg += tr("The document belongs to an external index"
|
||||||
|
"which I can't update. ");
|
||||||
|
}
|
||||||
|
msg += tr("Click Cancel to return to the list. "
|
||||||
|
"Click Ignore to show the preview anyway. ");
|
||||||
|
|
||||||
|
QMessageBox::StandardButtons bts =
|
||||||
|
QMessageBox::Ignore | QMessageBox::Cancel;
|
||||||
|
|
||||||
|
if (ixnotact &&ismainidx)
|
||||||
|
bts |= QMessageBox::Ok;
|
||||||
|
|
||||||
|
int rep =
|
||||||
|
QMessageBox::warning(0, tr("Warning"), msg, bts,
|
||||||
|
(ixnotact && ismainidx) ?
|
||||||
|
QMessageBox::Cancel : QMessageBox::NoButton);
|
||||||
|
|
||||||
|
if (m_indexerState == IXST_NOTRUNNING && rep == QMessageBox::Ok) {
|
||||||
|
LOGDEB(("Requesting index update for %s\n", doc.url.c_str()));
|
||||||
|
vector<Rcl::Doc> docs(1, doc);
|
||||||
|
updateIdxForDocs(docs);
|
||||||
|
}
|
||||||
|
if (rep != QMessageBox::Ignore)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open a preview window for a given document, or load it into new tab of
|
||||||
|
* existing window.
|
||||||
|
*
|
||||||
|
* @param docnum db query index
|
||||||
|
* @param mod keyboards modifiers like ControlButton, ShiftButton
|
||||||
|
*/
|
||||||
|
void RclMain::startPreview(int docnum, Rcl::Doc doc, int mod)
|
||||||
|
{
|
||||||
|
LOGDEB(("startPreview(%d, doc, %d)\n", docnum, mod));
|
||||||
|
|
||||||
|
if (!containerUpToDate(doc))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Do the zeitgeist thing
|
||||||
|
zg_send_event(ZGSEND_PREVIEW, doc);
|
||||||
|
|
||||||
|
if (mod & Qt::ShiftModifier) {
|
||||||
|
// User wants new preview window
|
||||||
|
curPreview = 0;
|
||||||
|
}
|
||||||
|
if (curPreview == 0) {
|
||||||
|
HighlightData hdata;
|
||||||
|
m_source->getTerms(hdata);
|
||||||
|
curPreview = new Preview(reslist->listId(), hdata);
|
||||||
|
|
||||||
|
if (curPreview == 0) {
|
||||||
|
QMessageBox::warning(0, tr("Warning"),
|
||||||
|
tr("Can't create preview window"),
|
||||||
|
QMessageBox::Ok,
|
||||||
|
QMessageBox::NoButton);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
connect(new QShortcut(quitKeySeq, curPreview), SIGNAL (activated()),
|
||||||
|
this, SLOT (fileExit()));
|
||||||
|
connect(curPreview, SIGNAL(previewClosed(Preview *)),
|
||||||
|
this, SLOT(previewClosed(Preview *)));
|
||||||
|
connect(curPreview, SIGNAL(wordSelect(QString)),
|
||||||
|
sSearch, SLOT(addTerm(QString)));
|
||||||
|
connect(curPreview, SIGNAL(showNext(Preview *, int, int)),
|
||||||
|
this, SLOT(previewNextInTab(Preview *, int, int)));
|
||||||
|
connect(curPreview, SIGNAL(showPrev(Preview *, int, int)),
|
||||||
|
this, SLOT(previewPrevInTab(Preview *, int, int)));
|
||||||
|
connect(curPreview, SIGNAL(previewExposed(Preview *, int, int)),
|
||||||
|
this, SLOT(previewExposed(Preview *, int, int)));
|
||||||
|
connect(curPreview, SIGNAL(saveDocToFile(Rcl::Doc)),
|
||||||
|
this, SLOT(saveDocToFile(Rcl::Doc)));
|
||||||
|
curPreview->setWindowTitle(getQueryDescription());
|
||||||
|
curPreview->show();
|
||||||
|
}
|
||||||
|
curPreview->makeDocCurrent(doc, docnum);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open a preview window for a given document, no linking to result list
|
||||||
|
*
|
||||||
|
* This is used to show ie parent documents, which have no corresponding
|
||||||
|
* entry in the result list.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void RclMain::startPreview(Rcl::Doc doc)
|
||||||
|
{
|
||||||
|
Preview *preview = new Preview(0, HighlightData());
|
||||||
|
if (preview == 0) {
|
||||||
|
QMessageBox::warning(0, tr("Warning"),
|
||||||
|
tr("Can't create preview window"),
|
||||||
|
QMessageBox::Ok,
|
||||||
|
QMessageBox::NoButton);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
connect(new QShortcut(quitKeySeq, preview), SIGNAL (activated()),
|
||||||
|
this, SLOT (fileExit()));
|
||||||
|
connect(preview, SIGNAL(wordSelect(QString)),
|
||||||
|
sSearch, SLOT(addTerm(QString)));
|
||||||
|
// Do the zeitgeist thing
|
||||||
|
zg_send_event(ZGSEND_PREVIEW, doc);
|
||||||
|
preview->show();
|
||||||
|
preview->makeDocCurrent(doc, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show next document from result list in current preview tab
|
||||||
|
void RclMain::previewNextInTab(Preview * w, int sid, int docnum)
|
||||||
|
{
|
||||||
|
previewPrevOrNextInTab(w, sid, docnum, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show previous document from result list in current preview tab
|
||||||
|
void RclMain::previewPrevInTab(Preview * w, int sid, int docnum)
|
||||||
|
{
|
||||||
|
previewPrevOrNextInTab(w, sid, docnum, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Combined next/prev from result list in current preview tab
|
||||||
|
void RclMain::previewPrevOrNextInTab(Preview * w, int sid, int docnum, bool nxt)
|
||||||
|
{
|
||||||
|
LOGDEB(("RclMain::previewNextInTab sid %d docnum %d, listId %d\n",
|
||||||
|
sid, docnum, reslist->listId()));
|
||||||
|
|
||||||
|
if (w == 0) // ??
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (sid != reslist->listId()) {
|
||||||
|
QMessageBox::warning(0, "Recoll",
|
||||||
|
tr("This search is not active any more"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nxt)
|
||||||
|
docnum++;
|
||||||
|
else
|
||||||
|
docnum--;
|
||||||
|
if (docnum < 0 || m_source.isNull() || docnum >= m_source->getResCnt()) {
|
||||||
|
QApplication::beep();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rcl::Doc doc;
|
||||||
|
if (!reslist->getDoc(docnum, doc)) {
|
||||||
|
QMessageBox::warning(0, "Recoll",
|
||||||
|
tr("Cannot retrieve document info from database"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
w->makeDocCurrent(doc, docnum, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preview tab exposed: if the preview comes from the currently
|
||||||
|
// displayed result list, tell reslist (to color the paragraph)
|
||||||
|
void RclMain::previewExposed(Preview *, int sid, int docnum)
|
||||||
|
{
|
||||||
|
LOGDEB2(("RclMain::previewExposed: sid %d docnum %d, m_sid %d\n",
|
||||||
|
sid, docnum, reslist->listId()));
|
||||||
|
if (sid != reslist->listId()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
reslist->previewExposed(docnum);
|
||||||
|
}
|
||||||
442
src/qtgui/rclm_view.cpp
Normal file
442
src/qtgui/rclm_view.cpp
Normal file
@ -0,0 +1,442 @@
|
|||||||
|
/* Copyright (C) 2005 J.F.Dockes
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the
|
||||||
|
* Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
#include "autoconfig.h"
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
#include <QMessageBox>
|
||||||
|
|
||||||
|
#include "debuglog.h"
|
||||||
|
#include "fileudi.h"
|
||||||
|
#include "execmd.h"
|
||||||
|
#include "transcode.h"
|
||||||
|
#include "docseqhist.h"
|
||||||
|
#include "docseqdb.h"
|
||||||
|
#include "internfile.h"
|
||||||
|
#include "rclmain_w.h"
|
||||||
|
#include "rclzg.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
// Start native viewer or preview for input Doc. This is used to allow
|
||||||
|
// using recoll from another app (e.g. Unity Scope) to view embedded
|
||||||
|
// result docs (docs with an ipath). . We act as a proxy to extract
|
||||||
|
// the data and start a viewer. The Url are encoded as
|
||||||
|
// file://path#ipath
|
||||||
|
void RclMain::viewUrl()
|
||||||
|
{
|
||||||
|
if (m_urltoview.isEmpty() || !rcldb)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QUrl qurl(m_urltoview);
|
||||||
|
LOGDEB(("RclMain::viewUrl: Path [%s] fragment [%s]\n",
|
||||||
|
(const char *)qurl.path().toLocal8Bit(),
|
||||||
|
(const char *)qurl.fragment().toLocal8Bit()));
|
||||||
|
|
||||||
|
/* In theory, the url might not be for a file managed by the fs
|
||||||
|
indexer so that the make_udi() call here would be
|
||||||
|
wrong(). When/if this happens we'll have to hide this part
|
||||||
|
inside internfile and have some url magic to indicate the
|
||||||
|
appropriate indexer/identification scheme */
|
||||||
|
string udi;
|
||||||
|
make_udi((const char *)qurl.path().toLocal8Bit(),
|
||||||
|
(const char *)qurl.fragment().toLocal8Bit(), udi);
|
||||||
|
|
||||||
|
Rcl::Doc doc;
|
||||||
|
Rcl::Doc idxdoc; // idxdoc.idxi == 0 -> works with base index only
|
||||||
|
if (!rcldb->getDoc(udi, idxdoc, doc) || doc.pc == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// StartNativeViewer needs a db source to call getEnclosing() on.
|
||||||
|
Rcl::Query *query = new Rcl::Query(rcldb);
|
||||||
|
DocSequenceDb *src =
|
||||||
|
new DocSequenceDb(RefCntr<Rcl::Query>(query), "",
|
||||||
|
RefCntr<Rcl::SearchData>(new Rcl::SearchData));
|
||||||
|
m_source = RefCntr<DocSequence>(src);
|
||||||
|
|
||||||
|
|
||||||
|
// Start a native viewer if the mimetype has one defined, else a
|
||||||
|
// preview.
|
||||||
|
string apptag;
|
||||||
|
doc.getmeta(Rcl::Doc::keyapptg, &apptag);
|
||||||
|
string viewer = theconfig->getMimeViewerDef(doc.mimetype, apptag,
|
||||||
|
prefs.useDesktopOpen);
|
||||||
|
if (viewer.empty()) {
|
||||||
|
startPreview(doc);
|
||||||
|
} else {
|
||||||
|
hide();
|
||||||
|
startNativeViewer(doc);
|
||||||
|
// We have a problem here because xdg-open will exit
|
||||||
|
// immediately after starting the command instead of waiting
|
||||||
|
// for it, so we can't wait either and we don't know when we
|
||||||
|
// can exit (deleting the temp file). As a bad workaround we
|
||||||
|
// sleep some time then exit. The alternative would be to just
|
||||||
|
// prevent the temp file deletion completely, leaving it
|
||||||
|
// around forever. Better to let the user save a copy if he
|
||||||
|
// wants I think.
|
||||||
|
sleep(30);
|
||||||
|
fileExit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Look for html browser. We make a special effort for html because it's
|
||||||
|
* used for reading help. This is only used if the normal approach
|
||||||
|
* (xdg-open etc.) failed */
|
||||||
|
static bool lookForHtmlBrowser(string &exefile)
|
||||||
|
{
|
||||||
|
static const char *htmlbrowserlist =
|
||||||
|
"opera google-chrome chromium-browser konqueror iceweasel firefox "
|
||||||
|
"mozilla netscape epiphany";
|
||||||
|
vector<string> blist;
|
||||||
|
stringToTokens(htmlbrowserlist, blist, " ");
|
||||||
|
|
||||||
|
const char *path = getenv("PATH");
|
||||||
|
if (path == 0)
|
||||||
|
path = "/bin:/usr/bin:/usr/bin/X11:/usr/X11R6/bin:/usr/local/bin";
|
||||||
|
|
||||||
|
// Look for each browser
|
||||||
|
for (vector<string>::const_iterator bit = blist.begin();
|
||||||
|
bit != blist.end(); bit++) {
|
||||||
|
if (ExecCmd::which(*bit, exefile, path))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
exefile.clear();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RclMain::openWith(Rcl::Doc doc, string cmdspec)
|
||||||
|
{
|
||||||
|
LOGDEB(("RclMain::openWith: %s\n", cmdspec.c_str()));
|
||||||
|
|
||||||
|
// Split the command line
|
||||||
|
vector<string> lcmd;
|
||||||
|
if (!stringToStrings(cmdspec, lcmd)) {
|
||||||
|
QMessageBox::warning(0, "Recoll",
|
||||||
|
tr("Bad desktop app spec for %1: [%2]\n"
|
||||||
|
"Please check the desktop file")
|
||||||
|
.arg(QString::fromUtf8(doc.mimetype.c_str()))
|
||||||
|
.arg(QString::fromLocal8Bit(cmdspec.c_str())));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for the command to execute in the exec path and the filters
|
||||||
|
// directory
|
||||||
|
string execname = lcmd.front();
|
||||||
|
lcmd.erase(lcmd.begin());
|
||||||
|
string url = doc.url;
|
||||||
|
string fn = fileurltolocalpath(doc.url);
|
||||||
|
|
||||||
|
// Try to keep the letters used more or less consistent with the reslist
|
||||||
|
// paragraph format.
|
||||||
|
map<string, string> subs;
|
||||||
|
subs["F"] = fn;
|
||||||
|
subs["f"] = fn;
|
||||||
|
subs["U"] = url;
|
||||||
|
subs["u"] = url;
|
||||||
|
|
||||||
|
execViewer(subs, false, execname, lcmd, cmdspec, doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RclMain::startNativeViewer(Rcl::Doc doc, int pagenum, QString term)
|
||||||
|
{
|
||||||
|
string apptag;
|
||||||
|
doc.getmeta(Rcl::Doc::keyapptg, &apptag);
|
||||||
|
LOGDEB(("RclMain::startNativeViewer: mtype [%s] apptag [%s] page %d "
|
||||||
|
"term [%s] url [%s] ipath [%s]\n",
|
||||||
|
doc.mimetype.c_str(), apptag.c_str(), pagenum,
|
||||||
|
(const char *)(term.toUtf8()), doc.url.c_str(), doc.ipath.c_str()
|
||||||
|
));
|
||||||
|
|
||||||
|
// Look for appropriate viewer
|
||||||
|
string cmdplusattr = theconfig->getMimeViewerDef(doc.mimetype, apptag,
|
||||||
|
prefs.useDesktopOpen);
|
||||||
|
if (cmdplusattr.empty()) {
|
||||||
|
QMessageBox::warning(0, "Recoll",
|
||||||
|
tr("No external viewer configured for mime type [")
|
||||||
|
+ doc.mimetype.c_str() + "]");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Separate command string and viewer attributes (if any)
|
||||||
|
ConfSimple viewerattrs;
|
||||||
|
string cmd;
|
||||||
|
theconfig->valueSplitAttributes(cmdplusattr, cmd, viewerattrs);
|
||||||
|
bool ignoreipath = false;
|
||||||
|
if (viewerattrs.get("ignoreipath", cmdplusattr))
|
||||||
|
ignoreipath = stringToBool(cmdplusattr);
|
||||||
|
|
||||||
|
// Split the command line
|
||||||
|
vector<string> lcmd;
|
||||||
|
if (!stringToStrings(cmd, lcmd)) {
|
||||||
|
QMessageBox::warning(0, "Recoll",
|
||||||
|
tr("Bad viewer command line for %1: [%2]\n"
|
||||||
|
"Please check the mimeview file")
|
||||||
|
.arg(QString::fromUtf8(doc.mimetype.c_str()))
|
||||||
|
.arg(QString::fromLocal8Bit(cmd.c_str())));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for the command to execute in the exec path and the filters
|
||||||
|
// directory
|
||||||
|
string execpath;
|
||||||
|
if (!ExecCmd::which(lcmd.front(), execpath)) {
|
||||||
|
execpath = theconfig->findFilter(lcmd.front());
|
||||||
|
// findFilter returns its input param if the filter is not in
|
||||||
|
// the normal places. As we already looked in the path, we
|
||||||
|
// have no use for a simple command name here (as opposed to
|
||||||
|
// mimehandler which will just let execvp do its thing). Erase
|
||||||
|
// execpath so that the user dialog will be started further
|
||||||
|
// down.
|
||||||
|
if (!execpath.compare(lcmd.front()))
|
||||||
|
execpath.erase();
|
||||||
|
|
||||||
|
// Specialcase text/html because of the help browser need
|
||||||
|
if (execpath.empty() && !doc.mimetype.compare("text/html") &&
|
||||||
|
apptag.empty()) {
|
||||||
|
if (lookForHtmlBrowser(execpath)) {
|
||||||
|
lcmd.clear();
|
||||||
|
lcmd.push_back(execpath);
|
||||||
|
lcmd.push_back("%u");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command not found: start the user dialog to help find another one:
|
||||||
|
if (execpath.empty()) {
|
||||||
|
QString mt = QString::fromUtf8(doc.mimetype.c_str());
|
||||||
|
QString message = tr("The viewer specified in mimeview for %1: %2"
|
||||||
|
" is not found.\nDo you want to start the "
|
||||||
|
" preferences dialog ?")
|
||||||
|
.arg(mt).arg(QString::fromLocal8Bit(lcmd.front().c_str()));
|
||||||
|
|
||||||
|
switch(QMessageBox::warning(0, "Recoll", message,
|
||||||
|
"Yes", "No", 0, 0, 1)) {
|
||||||
|
case 0:
|
||||||
|
showUIPrefs();
|
||||||
|
if (uiprefs)
|
||||||
|
uiprefs->showViewAction(mt);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// The user will have to click on the link again to try the
|
||||||
|
// new command.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Get rid of the command name. lcmd is now argv[1...n]
|
||||||
|
lcmd.erase(lcmd.begin());
|
||||||
|
|
||||||
|
// Process the command arguments to determine if we need to create
|
||||||
|
// a temporary file.
|
||||||
|
|
||||||
|
// If the command has a %i parameter it will manage the
|
||||||
|
// un-embedding. Else if ipath is not empty, we need a temp file.
|
||||||
|
// This can be overridden with the "ignoreipath" attribute
|
||||||
|
bool groksipath = (cmd.find("%i") != string::npos) || ignoreipath;
|
||||||
|
|
||||||
|
// wantsfile: do we actually need a local file ? The only other
|
||||||
|
// case here is an url %u (ie: for web history).
|
||||||
|
bool wantsfile = cmd.find("%f") != string::npos && urlisfileurl(doc.url);
|
||||||
|
bool wantsparentfile = cmd.find("%F") != string::npos &&
|
||||||
|
urlisfileurl(doc.url);
|
||||||
|
|
||||||
|
if (wantsfile && wantsparentfile) {
|
||||||
|
QMessageBox::warning(0, "Recoll",
|
||||||
|
tr("Viewer command line for %1 specifies both "
|
||||||
|
"file and parent file value: unsupported")
|
||||||
|
.arg(QString::fromUtf8(doc.mimetype.c_str())));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string url = doc.url;
|
||||||
|
string fn = fileurltolocalpath(doc.url);
|
||||||
|
Rcl::Doc pdoc;
|
||||||
|
if (wantsparentfile) {
|
||||||
|
// We want the path for the parent document. For example to
|
||||||
|
// open the chm file, not the internal page. Note that we just
|
||||||
|
// override the other file name in this case.
|
||||||
|
if (m_source.isNull() || !m_source->getEnclosing(doc, pdoc)) {
|
||||||
|
QMessageBox::warning(0, "Recoll",
|
||||||
|
tr("Cannot find parent document"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Override fn with the parent's :
|
||||||
|
fn = fileurltolocalpath(pdoc.url);
|
||||||
|
|
||||||
|
// If the parent document has an ipath too, we need to create
|
||||||
|
// a temp file even if the command takes an ipath
|
||||||
|
// parameter. We have no viewer which could handle a double
|
||||||
|
// embedding. Will have to change if such a one appears.
|
||||||
|
if (!pdoc.ipath.empty()) {
|
||||||
|
groksipath = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool istempfile = false;
|
||||||
|
|
||||||
|
LOGDEB(("RclMain::startNV: groksipath %d wantsf %d wantsparentf %d\n",
|
||||||
|
groksipath, wantsfile, wantsparentfile));
|
||||||
|
|
||||||
|
// If the command wants a file but this is not a file url, or
|
||||||
|
// there is an ipath that it won't understand, we need a temp file:
|
||||||
|
theconfig->setKeyDir(path_getfather(fn));
|
||||||
|
if (((wantsfile || wantsparentfile) && fn.empty()) ||
|
||||||
|
(!groksipath && !doc.ipath.empty())) {
|
||||||
|
TempFile temp;
|
||||||
|
Rcl::Doc& thedoc = wantsparentfile ? pdoc : doc;
|
||||||
|
if (!FileInterner::idocToFile(temp, string(), theconfig, thedoc)) {
|
||||||
|
QMessageBox::warning(0, "Recoll",
|
||||||
|
tr("Cannot extract document or create "
|
||||||
|
"temporary file"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
istempfile = true;
|
||||||
|
rememberTempFile(temp);
|
||||||
|
fn = temp->filename();
|
||||||
|
url = string("file://") + fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If using an actual file, check that it exists, and if it is
|
||||||
|
// compressed, we may need an uncompressed version
|
||||||
|
if (!fn.empty() && theconfig->mimeViewerNeedsUncomp(doc.mimetype)) {
|
||||||
|
if (access(fn.c_str(), R_OK) != 0) {
|
||||||
|
QMessageBox::warning(0, "Recoll",
|
||||||
|
tr("Can't access file: ") +
|
||||||
|
QString::fromLocal8Bit(fn.c_str()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
TempFile temp;
|
||||||
|
if (FileInterner::isCompressed(fn, theconfig)) {
|
||||||
|
if (!FileInterner::maybeUncompressToTemp(temp, fn, theconfig,
|
||||||
|
doc)) {
|
||||||
|
QMessageBox::warning(0, "Recoll",
|
||||||
|
tr("Can't uncompress file: ") +
|
||||||
|
QString::fromLocal8Bit(fn.c_str()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!temp.isNull()) {
|
||||||
|
rememberTempFile(temp);
|
||||||
|
fn = temp->filename();
|
||||||
|
url = string("file://") + fn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are not called with a page number (which would happen for a call
|
||||||
|
// from the snippets window), see if we can compute a page number anyway.
|
||||||
|
if (pagenum == -1) {
|
||||||
|
pagenum = 1;
|
||||||
|
string lterm;
|
||||||
|
if (m_source.isNotNull())
|
||||||
|
pagenum = m_source->getFirstMatchPage(doc, lterm);
|
||||||
|
if (pagenum == -1)
|
||||||
|
pagenum = 1;
|
||||||
|
else // We get the match term used to compute the page
|
||||||
|
term = QString::fromUtf8(lterm.c_str());
|
||||||
|
}
|
||||||
|
char cpagenum[20];
|
||||||
|
sprintf(cpagenum, "%d", pagenum);
|
||||||
|
|
||||||
|
|
||||||
|
// Substitute %xx inside arguments
|
||||||
|
string efftime;
|
||||||
|
if (!doc.dmtime.empty() || !doc.fmtime.empty()) {
|
||||||
|
efftime = doc.dmtime.empty() ? doc.fmtime : doc.dmtime;
|
||||||
|
} else {
|
||||||
|
efftime = "0";
|
||||||
|
}
|
||||||
|
// Try to keep the letters used more or less consistent with the reslist
|
||||||
|
// paragraph format.
|
||||||
|
map<string, string> subs;
|
||||||
|
subs["D"] = efftime;
|
||||||
|
subs["f"] = fn;
|
||||||
|
subs["F"] = fn;
|
||||||
|
subs["i"] = FileInterner::getLastIpathElt(doc.ipath);
|
||||||
|
subs["M"] = doc.mimetype;
|
||||||
|
subs["p"] = cpagenum;
|
||||||
|
subs["s"] = (const char*)term.toLocal8Bit();
|
||||||
|
subs["U"] = url;
|
||||||
|
subs["u"] = url;
|
||||||
|
// Let %(xx) access all metadata.
|
||||||
|
for (map<string,string>::const_iterator it = doc.meta.begin();
|
||||||
|
it != doc.meta.end(); it++) {
|
||||||
|
subs[it->first] = it->second;
|
||||||
|
}
|
||||||
|
execViewer(subs, istempfile, execpath, lcmd, cmd, doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RclMain::execViewer(const map<string, string>& subs, bool istempfile,
|
||||||
|
const string& execpath,
|
||||||
|
const vector<string>& _lcmd, const string& cmd,
|
||||||
|
Rcl::Doc doc)
|
||||||
|
{
|
||||||
|
string ncmd;
|
||||||
|
vector<string> lcmd;
|
||||||
|
for (vector<string>::const_iterator it = _lcmd.begin();
|
||||||
|
it != _lcmd.end(); it++) {
|
||||||
|
pcSubst(*it, ncmd, subs);
|
||||||
|
LOGDEB(("%s->%s\n", it->c_str(), ncmd.c_str()));
|
||||||
|
lcmd.push_back(ncmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also substitute inside the unsplitted command line and display
|
||||||
|
// in status bar
|
||||||
|
pcSubst(cmd, ncmd, subs);
|
||||||
|
ncmd += " &";
|
||||||
|
QStatusBar *stb = statusBar();
|
||||||
|
if (stb) {
|
||||||
|
string fcharset = theconfig->getDefCharset(true);
|
||||||
|
string prcmd;
|
||||||
|
transcode(ncmd, prcmd, fcharset, "UTF-8");
|
||||||
|
QString msg = tr("Executing: [") +
|
||||||
|
QString::fromUtf8(prcmd.c_str()) + "]";
|
||||||
|
stb->showMessage(msg, 10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!istempfile)
|
||||||
|
historyEnterDoc(g_dynconf, doc.meta[Rcl::Doc::keyudi]);
|
||||||
|
|
||||||
|
// Do the zeitgeist thing
|
||||||
|
zg_send_event(ZGSEND_OPEN, doc);
|
||||||
|
|
||||||
|
// We keep pushing back and never deleting. This can't be good...
|
||||||
|
ExecCmd *ecmd = new ExecCmd;
|
||||||
|
m_viewers.push_back(ecmd);
|
||||||
|
ecmd->startExec(execpath, lcmd, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RclMain::startManual()
|
||||||
|
{
|
||||||
|
startManual(string());
|
||||||
|
}
|
||||||
|
|
||||||
|
void RclMain::startManual(const string& index)
|
||||||
|
{
|
||||||
|
Rcl::Doc doc;
|
||||||
|
doc.url = "file://";
|
||||||
|
doc.url = path_cat(doc.url, theconfig->getDatadir());
|
||||||
|
doc.url = path_cat(doc.url, "doc");
|
||||||
|
doc.url = path_cat(doc.url, "usermanual.html");
|
||||||
|
LOGDEB(("RclMain::startManual: help index is %s\n",
|
||||||
|
index.empty()?"(null)":index.c_str()));
|
||||||
|
if (!index.empty()) {
|
||||||
|
doc.url += "#";
|
||||||
|
doc.url += index;
|
||||||
|
}
|
||||||
|
doc.mimetype = "text/html";
|
||||||
|
startNativeViewer(doc);
|
||||||
|
}
|
||||||
390
src/qtgui/rclm_wins.cpp
Normal file
390
src/qtgui/rclm_wins.cpp
Normal file
@ -0,0 +1,390 @@
|
|||||||
|
/* Copyright (C) 2005 J.F.Dockes
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the
|
||||||
|
* Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
#include "autoconfig.h"
|
||||||
|
|
||||||
|
#include <QShortcut>
|
||||||
|
#include <QMessageBox>
|
||||||
|
|
||||||
|
#include "debuglog.h"
|
||||||
|
#include "internfile.h"
|
||||||
|
#include "listdialog.h"
|
||||||
|
#include "confgui/confguiindex.h"
|
||||||
|
#include "idxsched.h"
|
||||||
|
#include "crontool.h"
|
||||||
|
#include "rtitool.h"
|
||||||
|
#include "snippets_w.h"
|
||||||
|
#include "fragbuts.h"
|
||||||
|
#include "rclmain_w.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
static const QKeySequence quitKeySeq("Ctrl+q");
|
||||||
|
static const QKeySequence closeKeySeq("Ctrl+w");
|
||||||
|
|
||||||
|
// Open advanced search dialog.
|
||||||
|
void RclMain::showAdvSearchDialog()
|
||||||
|
{
|
||||||
|
if (asearchform == 0) {
|
||||||
|
asearchform = new AdvSearch(0);
|
||||||
|
if (asearchform == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
connect(new QShortcut(quitKeySeq, asearchform), SIGNAL (activated()),
|
||||||
|
this, SLOT (fileExit()));
|
||||||
|
|
||||||
|
connect(asearchform,
|
||||||
|
SIGNAL(startSearch(RefCntr<Rcl::SearchData>, bool)),
|
||||||
|
this, SLOT(startSearch(RefCntr<Rcl::SearchData>, bool)));
|
||||||
|
asearchform->show();
|
||||||
|
} else {
|
||||||
|
// Close and reopen, in hope that makes us visible...
|
||||||
|
asearchform->close();
|
||||||
|
asearchform->show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RclMain::showSpellDialog()
|
||||||
|
{
|
||||||
|
if (spellform == 0) {
|
||||||
|
spellform = new SpellW(0);
|
||||||
|
connect(new QShortcut(quitKeySeq, spellform), SIGNAL (activated()),
|
||||||
|
this, SLOT (fileExit()));
|
||||||
|
connect(spellform, SIGNAL(wordSelect(QString)),
|
||||||
|
sSearch, SLOT(addTerm(QString)));
|
||||||
|
spellform->show();
|
||||||
|
} else {
|
||||||
|
// Close and reopen, in hope that makes us visible...
|
||||||
|
spellform->close();
|
||||||
|
spellform->show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RclMain::showFragButs()
|
||||||
|
{
|
||||||
|
if (fragbuts && fragbuts->isStale(0)) {
|
||||||
|
deleteZ(fragbuts);
|
||||||
|
}
|
||||||
|
if (fragbuts == 0) {
|
||||||
|
fragbuts = new FragButs(0);
|
||||||
|
if (fragbuts->ok()) {
|
||||||
|
fragbuts->show();
|
||||||
|
connect(fragbuts, SIGNAL(fragmentsChanged()),
|
||||||
|
this, SLOT(onFragmentsChanged()));
|
||||||
|
} else {
|
||||||
|
delete fragbuts;
|
||||||
|
fragbuts = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Close and reopen, in hope that makes us visible...
|
||||||
|
fragbuts->close();
|
||||||
|
fragbuts->show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RclMain::showIndexConfig()
|
||||||
|
{
|
||||||
|
showIndexConfig(false);
|
||||||
|
}
|
||||||
|
void RclMain::execIndexConfig()
|
||||||
|
{
|
||||||
|
showIndexConfig(true);
|
||||||
|
}
|
||||||
|
void RclMain::showIndexConfig(bool modal)
|
||||||
|
{
|
||||||
|
LOGDEB(("showIndexConfig()\n"));
|
||||||
|
if (indexConfig == 0) {
|
||||||
|
indexConfig = new ConfIndexW(0, theconfig);
|
||||||
|
connect(new QShortcut(quitKeySeq, indexConfig), SIGNAL (activated()),
|
||||||
|
this, SLOT (fileExit()));
|
||||||
|
} else {
|
||||||
|
// Close and reopen, in hope that makes us visible...
|
||||||
|
indexConfig->close();
|
||||||
|
indexConfig->reloadPanels();
|
||||||
|
}
|
||||||
|
if (modal) {
|
||||||
|
indexConfig->exec();
|
||||||
|
indexConfig->setModal(false);
|
||||||
|
} else {
|
||||||
|
indexConfig->show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RclMain::showIndexSched()
|
||||||
|
{
|
||||||
|
showIndexSched(false);
|
||||||
|
}
|
||||||
|
void RclMain::execIndexSched()
|
||||||
|
{
|
||||||
|
showIndexSched(true);
|
||||||
|
}
|
||||||
|
void RclMain::showIndexSched(bool modal)
|
||||||
|
{
|
||||||
|
LOGDEB(("showIndexSched()\n"));
|
||||||
|
if (indexSched == 0) {
|
||||||
|
indexSched = new IdxSchedW(this);
|
||||||
|
connect(new QShortcut(quitKeySeq, indexSched), SIGNAL (activated()),
|
||||||
|
this, SLOT (fileExit()));
|
||||||
|
connect(indexSched->cronCLB, SIGNAL(clicked()),
|
||||||
|
this, SLOT(execCronTool()));
|
||||||
|
if (theconfig && theconfig->isDefaultConfig()) {
|
||||||
|
#ifdef RCL_MONITOR
|
||||||
|
connect(indexSched->rtidxCLB, SIGNAL(clicked()),
|
||||||
|
this, SLOT(execRTITool()));
|
||||||
|
#else
|
||||||
|
indexSched->rtidxCLB->setEnabled(false);
|
||||||
|
indexSched->rtidxCLB->setToolTip(tr("Disabled because the real time indexer was not compiled in."));
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
indexSched->rtidxCLB->setEnabled(false);
|
||||||
|
indexSched->rtidxCLB->setToolTip(tr("This configuration tool only works for the main index."));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Close and reopen, in hope that makes us visible...
|
||||||
|
indexSched->close();
|
||||||
|
}
|
||||||
|
if (modal) {
|
||||||
|
indexSched->exec();
|
||||||
|
indexSched->setModal(false);
|
||||||
|
} else {
|
||||||
|
indexSched->show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RclMain::showCronTool()
|
||||||
|
{
|
||||||
|
showCronTool(false);
|
||||||
|
}
|
||||||
|
void RclMain::execCronTool()
|
||||||
|
{
|
||||||
|
showCronTool(true);
|
||||||
|
}
|
||||||
|
void RclMain::showCronTool(bool modal)
|
||||||
|
{
|
||||||
|
LOGDEB(("showCronTool()\n"));
|
||||||
|
if (cronTool == 0) {
|
||||||
|
cronTool = new CronToolW(0);
|
||||||
|
connect(new QShortcut(quitKeySeq, cronTool), SIGNAL (activated()),
|
||||||
|
this, SLOT (fileExit()));
|
||||||
|
} else {
|
||||||
|
// Close and reopen, in hope that makes us visible...
|
||||||
|
cronTool->close();
|
||||||
|
}
|
||||||
|
if (modal) {
|
||||||
|
cronTool->exec();
|
||||||
|
cronTool->setModal(false);
|
||||||
|
} else {
|
||||||
|
cronTool->show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RclMain::showRTITool()
|
||||||
|
{
|
||||||
|
showRTITool(false);
|
||||||
|
}
|
||||||
|
void RclMain::execRTITool()
|
||||||
|
{
|
||||||
|
showRTITool(true);
|
||||||
|
}
|
||||||
|
void RclMain::showRTITool(bool modal)
|
||||||
|
{
|
||||||
|
LOGDEB(("showRTITool()\n"));
|
||||||
|
if (rtiTool == 0) {
|
||||||
|
rtiTool = new RTIToolW(0);
|
||||||
|
connect(new QShortcut(quitKeySeq, rtiTool), SIGNAL (activated()),
|
||||||
|
this, SLOT (fileExit()));
|
||||||
|
} else {
|
||||||
|
// Close and reopen, in hope that makes us visible...
|
||||||
|
rtiTool->close();
|
||||||
|
}
|
||||||
|
if (modal) {
|
||||||
|
rtiTool->exec();
|
||||||
|
rtiTool->setModal(false);
|
||||||
|
} else {
|
||||||
|
rtiTool->show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RclMain::showUIPrefs()
|
||||||
|
{
|
||||||
|
if (uiprefs == 0) {
|
||||||
|
uiprefs = new UIPrefsDialog(this);
|
||||||
|
connect(new QShortcut(quitKeySeq, uiprefs), SIGNAL (activated()),
|
||||||
|
this, SLOT (fileExit()));
|
||||||
|
connect(uiprefs, SIGNAL(uiprefsDone()), this, SLOT(setUIPrefs()));
|
||||||
|
connect(this, SIGNAL(stemLangChanged(const QString&)),
|
||||||
|
uiprefs, SLOT(setStemLang(const QString&)));
|
||||||
|
} else {
|
||||||
|
// Close and reopen, in hope that makes us visible...
|
||||||
|
uiprefs->close();
|
||||||
|
}
|
||||||
|
uiprefs->show();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RclMain::showExtIdxDialog()
|
||||||
|
{
|
||||||
|
if (uiprefs == 0) {
|
||||||
|
uiprefs = new UIPrefsDialog(this);
|
||||||
|
connect(new QShortcut(quitKeySeq, uiprefs), SIGNAL (activated()),
|
||||||
|
this, SLOT (fileExit()));
|
||||||
|
connect(uiprefs, SIGNAL(uiprefsDone()), this, SLOT(setUIPrefs()));
|
||||||
|
} else {
|
||||||
|
// Close and reopen, in hope that makes us visible...
|
||||||
|
uiprefs->close();
|
||||||
|
}
|
||||||
|
uiprefs->tabWidget->setCurrentIndex(3);
|
||||||
|
uiprefs->show();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RclMain::showAboutDialog()
|
||||||
|
{
|
||||||
|
string vstring = Rcl::version_string() +
|
||||||
|
string("<br> http://www.recoll.org") +
|
||||||
|
string("<br> http://www.xapian.org");
|
||||||
|
QMessageBox::information(this, tr("About Recoll"), vstring.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void RclMain::showMissingHelpers()
|
||||||
|
{
|
||||||
|
string miss;
|
||||||
|
if (!theconfig->getMissingHelperDesc(miss)) {
|
||||||
|
QMessageBox::information(this, "", tr("Indexing did not run yet"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QString msg = QString::fromUtf8("<p>") +
|
||||||
|
tr("External applications/commands needed for your file types "
|
||||||
|
"and not found, as stored by the last indexing pass in ");
|
||||||
|
msg += "<i>";
|
||||||
|
msg += QString::fromLocal8Bit(theconfig->getConfDir().c_str());
|
||||||
|
msg += "/missing</i>:<pre>\n";
|
||||||
|
if (!miss.empty()) {
|
||||||
|
msg += QString::fromUtf8(miss.c_str());
|
||||||
|
} else {
|
||||||
|
msg += tr("No helpers found missing");
|
||||||
|
}
|
||||||
|
msg += "</pre>";
|
||||||
|
QMessageBox::information(this, tr("Missing helper programs"), msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RclMain::showActiveTypes()
|
||||||
|
{
|
||||||
|
if (rcldb == 0) {
|
||||||
|
QMessageBox::warning(0, tr("Error"),
|
||||||
|
tr("Index not open"),
|
||||||
|
QMessageBox::Ok,
|
||||||
|
QMessageBox::NoButton);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// All mime types in index.
|
||||||
|
vector<string> vdbtypes;
|
||||||
|
if (!rcldb->getAllDbMimeTypes(vdbtypes)) {
|
||||||
|
QMessageBox::warning(0, tr("Error"),
|
||||||
|
tr("Index query error"),
|
||||||
|
QMessageBox::Ok,
|
||||||
|
QMessageBox::NoButton);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
set<string> mtypesfromdb;
|
||||||
|
mtypesfromdb.insert(vdbtypes.begin(), vdbtypes.end());
|
||||||
|
|
||||||
|
// All types listed in mimeconf:
|
||||||
|
vector<string> mtypesfromconfig = theconfig->getAllMimeTypes();
|
||||||
|
|
||||||
|
// Intersect file system types with config types (those not in the
|
||||||
|
// config can be indexed by name, not by content)
|
||||||
|
set<string> mtypesfromdbconf;
|
||||||
|
for (vector<string>::const_iterator it = mtypesfromconfig.begin();
|
||||||
|
it != mtypesfromconfig.end(); it++) {
|
||||||
|
if (mtypesfromdb.find(*it) != mtypesfromdb.end())
|
||||||
|
mtypesfromdbconf.insert(*it);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Substract the types for missing helpers (the docs are indexed
|
||||||
|
// by name only):
|
||||||
|
string miss;
|
||||||
|
if (theconfig->getMissingHelperDesc(miss) && !miss.empty()) {
|
||||||
|
FIMissingStore st(miss);
|
||||||
|
map<string, set<string> >::const_iterator it;
|
||||||
|
for (it = st.m_typesForMissing.begin();
|
||||||
|
it != st.m_typesForMissing.end(); it++) {
|
||||||
|
set<string>::const_iterator it1;
|
||||||
|
for (it1 = it->second.begin();
|
||||||
|
it1 != it->second.end(); it1++) {
|
||||||
|
set<string>::iterator it2 = mtypesfromdbconf.find(*it1);
|
||||||
|
if (it2 != mtypesfromdbconf.end())
|
||||||
|
mtypesfromdbconf.erase(it2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ListDialog dialog;
|
||||||
|
dialog.setWindowTitle(tr("Indexed MIME Types"));
|
||||||
|
|
||||||
|
// Turn the result into a string and display
|
||||||
|
dialog.groupBox->setTitle(tr("Content has been indexed for these mime types:"));
|
||||||
|
|
||||||
|
// We replace the list with an editor so that the user can copy/paste
|
||||||
|
delete dialog.listWidget;
|
||||||
|
QTextEdit *editor = new QTextEdit(dialog.groupBox);
|
||||||
|
editor->setReadOnly(true);
|
||||||
|
dialog.horizontalLayout->addWidget(editor);
|
||||||
|
|
||||||
|
for (set<string>::const_iterator it = mtypesfromdbconf.begin();
|
||||||
|
it != mtypesfromdbconf.end(); it++) {
|
||||||
|
editor->append(QString::fromUtf8(it->c_str()));
|
||||||
|
}
|
||||||
|
editor->moveCursor(QTextCursor::Start);
|
||||||
|
editor->ensureCursorVisible();
|
||||||
|
dialog.exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RclMain::newDupsW(const Rcl::Doc, const vector<Rcl::Doc> dups)
|
||||||
|
{
|
||||||
|
ListDialog dialog;
|
||||||
|
dialog.setWindowTitle(tr("Duplicate documents"));
|
||||||
|
|
||||||
|
dialog.groupBox->setTitle(tr("These Urls ( | ipath) share the same"
|
||||||
|
" content:"));
|
||||||
|
// We replace the list with an editor so that the user can copy/paste
|
||||||
|
delete dialog.listWidget;
|
||||||
|
QTextEdit *editor = new QTextEdit(dialog.groupBox);
|
||||||
|
editor->setReadOnly(true);
|
||||||
|
dialog.horizontalLayout->addWidget(editor);
|
||||||
|
|
||||||
|
for (vector<Rcl::Doc>::const_iterator it = dups.begin();
|
||||||
|
it != dups.end(); it++) {
|
||||||
|
if (it->ipath.empty())
|
||||||
|
editor->append(QString::fromLocal8Bit(it->url.c_str()));
|
||||||
|
else
|
||||||
|
editor->append(QString::fromLocal8Bit(it->url.c_str()) + " | " +
|
||||||
|
QString::fromUtf8(it->ipath.c_str()));
|
||||||
|
}
|
||||||
|
editor->moveCursor(QTextCursor::Start);
|
||||||
|
editor->ensureCursorVisible();
|
||||||
|
dialog.exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RclMain::showSnippets(Rcl::Doc doc)
|
||||||
|
{
|
||||||
|
SnippetsW *sp = new SnippetsW(doc, m_source);
|
||||||
|
connect(sp, SIGNAL(startNativeViewer(Rcl::Doc, int, QString)),
|
||||||
|
this, SLOT(startNativeViewer(Rcl::Doc, int, QString)));
|
||||||
|
connect(new QShortcut(quitKeySeq, sp), SIGNAL (activated()),
|
||||||
|
this, SLOT (fileExit()));
|
||||||
|
connect(new QShortcut(closeKeySeq, sp), SIGNAL (activated()),
|
||||||
|
sp, SLOT (close()));
|
||||||
|
sp->show();
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@ -52,7 +52,11 @@ SOURCES += \
|
|||||||
ptrans_w.cpp \
|
ptrans_w.cpp \
|
||||||
rclhelp.cpp \
|
rclhelp.cpp \
|
||||||
rclmain_w.cpp \
|
rclmain_w.cpp \
|
||||||
|
rclm_idx.cpp \
|
||||||
|
rclm_preview.cpp \
|
||||||
rclm_saveload.cpp \
|
rclm_saveload.cpp \
|
||||||
|
rclm_view.cpp \
|
||||||
|
rclm_wins.cpp \
|
||||||
rclzg.cpp \
|
rclzg.cpp \
|
||||||
respopup.cpp \
|
respopup.cpp \
|
||||||
reslist.cpp \
|
reslist.cpp \
|
||||||
|
|||||||
@ -40,7 +40,7 @@ class DocSequenceDb : public DocSequence {
|
|||||||
virtual int getFirstMatchPage(Rcl::Doc&, std::string& term);
|
virtual int getFirstMatchPage(Rcl::Doc&, std::string& term);
|
||||||
virtual bool docDups(const Rcl::Doc& doc, std::vector<Rcl::Doc>& dups);
|
virtual bool docDups(const Rcl::Doc& doc, std::vector<Rcl::Doc>& dups);
|
||||||
virtual string getDescription();
|
virtual string getDescription();
|
||||||
virtual list<string> expand(Rcl::Doc &doc);
|
virtual std::list<std::string> expand(Rcl::Doc &doc);
|
||||||
virtual bool canFilter() {return true;}
|
virtual bool canFilter() {return true;}
|
||||||
virtual bool setFiltSpec(const DocSeqFiltSpec &filtspec);
|
virtual bool setFiltSpec(const DocSeqFiltSpec &filtspec);
|
||||||
virtual bool canSort() {return true;}
|
virtual bool canSort() {return true;}
|
||||||
|
|||||||
@ -58,9 +58,9 @@ private:
|
|||||||
RclDynConf *m_hist;
|
RclDynConf *m_hist;
|
||||||
int m_prevnum;
|
int m_prevnum;
|
||||||
long m_prevtime;
|
long m_prevtime;
|
||||||
string m_description; // This is just an nls translated 'doc history'
|
std::string m_description; // This is just an nls translated 'doc history'
|
||||||
list<RclDHistoryEntry> m_hlist;
|
std::list<RclDHistoryEntry> m_hlist;
|
||||||
list<RclDHistoryEntry>::const_iterator m_it;
|
std::list<RclDHistoryEntry>::const_iterator m_it;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern bool historyEnterDoc(RclDynConf *dncf, const string& udi);
|
extern bool historyEnterDoc(RclDynConf *dncf, const string& udi);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user