From eed200a4bd30ff767cac4231c54421e072605591 Mon Sep 17 00:00:00 2001 From: dockes Date: Fri, 20 Jan 2006 12:46:51 +0000 Subject: [PATCH] separated code from design by subclassing recollmain --- src/qtgui/main.cpp | 8 +- src/qtgui/rclmain.cpp | 864 ++++++++++++++++++++++++++++++++++++++ src/qtgui/rclmain.h | 80 ++++ src/qtgui/recoll.pro | 12 +- src/qtgui/recoll_fr.ts | 217 ++++------ src/qtgui/recollmain.ui | 68 +-- src/qtgui/recollmain.ui.h | 825 +----------------------------------- 7 files changed, 1086 insertions(+), 988 deletions(-) create mode 100644 src/qtgui/rclmain.cpp create mode 100644 src/qtgui/rclmain.h diff --git a/src/qtgui/main.cpp b/src/qtgui/main.cpp index ec91eddc..a9099de5 100644 --- a/src/qtgui/main.cpp +++ b/src/qtgui/main.cpp @@ -1,5 +1,5 @@ #ifndef lint -static char rcsid[] = "@(#$Id: main.cpp,v 1.30 2006-01-20 10:01:59 dockes Exp $ (C) 2005 J.F.Dockes"; +static char rcsid[] = "@(#$Id: main.cpp,v 1.31 2006-01-20 12:46:50 dockes Exp $ (C) 2005 J.F.Dockes"; #endif #include @@ -28,7 +28,7 @@ using Rcl::AdvSearchData; #include "history.h" #include "debuglog.h" -#include "recollmain.h" +#include "rclmain.h" static const char *recoll_datadir = RECOLL_DATADIR; @@ -39,7 +39,7 @@ string tmpdir; string iconsdir; RclDHistory *history; static string dbdir; -static RecollMain *mainWindow; +static RclMainBase *mainWindow; static string recollsharedir; ///////////////////////// @@ -147,7 +147,7 @@ int main( int argc, char ** argv ) rwSettings(false); // Create main window and set its size to previous session's - RecollMain w; + RclMain w; mainWindow = &w; QSize s(prefs_mainwidth, prefs_mainheight); w.resize(s); diff --git a/src/qtgui/rclmain.cpp b/src/qtgui/rclmain.cpp new file mode 100644 index 00000000..a802f7cc --- /dev/null +++ b/src/qtgui/rclmain.cpp @@ -0,0 +1,864 @@ +/**************************************************************************** + ** ui.h extension file, included from the uic-generated form implementation. + ** + ** If you want to add, delete, or rename functions or slots, use + ** Qt Designer to update this file, preserving your code. + ** + ** You should not define a constructor or destructor in this file. + ** Instead, write your code in functions called init() and destroy(). + ** These will automatically be called by the form's constructor and + ** destructor. + *****************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include +#ifndef NO_NAMESPACES +using std::pair; +#endif /* NO_NAMESPACES */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "recoll.h" +#include "debuglog.h" +#include "mimehandler.h" +#include "pathut.h" +#include "smallut.h" +#include "plaintorich.h" +#include "advsearch.h" +#include "rclversion.h" +#include "sortseq.h" +#include "uiprefs.h" + +#include "rclmain.h" +#include "moc_rclmain.cpp" + +extern "C" int XFlush(void *); + +#ifndef MIN +#define MIN(A,B) ((A) < (B) ? (A) : (B)) +#endif + +void RclMain::init() +{ + reslist_winfirst = -1; + reslist_mouseDrag = false; + reslist_mouseDown = false; + reslist_par = -1; + reslist_car = -1; + reslist_waitingdbl = false; + reslist_dblclck = false; + curPreview = 0; + asearchform = 0; + sortform = 0; + docsource = 0; + sortwidth = 0; + uiprefs = 0; + + // We manage pgup/down, but let ie the arrows for the editor to process + reslistTE->installEventFilter(this); + reslistTE->viewport()->installEventFilter(this); + // reslistTE->viewport()->setFocusPolicy(QWidget::NoFocus); + + // Set the focus to the search terms entry: + queryText->setFocus(); + + // Set result list font according to user preferences. + if (prefs_reslistfontfamily.length()) { + QFont nfont(prefs_reslistfontfamily, prefs_reslistfontsize); + reslistTE->setFont(nfont); + } +} + +// We also want to get rid of the advanced search form and previews +// when we exit (not our children so that it's not systematically +// created over the main form). +bool RclMain::close(bool) +{ + fileExit(); + return false; +} + +//#define SHOWEVENTS +#if defined(SHOWEVENTS) +static const char *eventTypeToStr(int tp) +{ + switch (tp) { + case 0: return "None"; + case 1: return "Timer"; + case 2: return "MouseButtonPress"; + case 3: return "MouseButtonRelease"; + case 4: return "MouseButtonDblClick"; + case 5: return "MouseMove"; + case 6: return "KeyPress"; + case 7: return "KeyRelease"; + case 8: return "FocusIn"; + case 9: return "FocusOut"; + case 10: return "Enter"; + case 11: return "Leave"; + case 12: return "Paint"; + case 13: return "Move"; + case 14: return "Resize"; + case 15: return "Create"; + case 16: return "Destroy"; + case 17: return "Show"; + case 18: return "Hide"; + case 19: return "Close"; + case 20: return "Quit"; + case 21: return "Reparent"; + case 22: return "ShowMinimized"; + case 23: return "ShowNormal"; + case 24: return "WindowActivate"; + case 25: return "WindowDeactivate"; + case 26: return "ShowToParent"; + case 27: return "HideToParent"; + case 28: return "ShowMaximized"; + case 29: return "ShowFullScreen"; + case 30: return "Accel"; + case 31: return "Wheel"; + case 32: return "AccelAvailable"; + case 33: return "CaptionChange"; + case 34: return "IconChange"; + case 35: return "ParentFontChange"; + case 36: return "ApplicationFontChange"; + case 37: return "ParentPaletteChange"; + case 38: return "ApplicationPaletteChange"; + case 39: return "PaletteChange"; + case 40: return "Clipboard"; + case 42: return "Speech"; + case 50: return "SockAct"; + case 51: return "AccelOverride"; + case 52: return "DeferredDelete"; + case 60: return "DragEnter"; + case 61: return "DragMove"; + case 62: return "DragLeave"; + case 63: return "Drop"; + case 64: return "DragResponse"; + case 70: return "ChildInserted"; + case 71: return "ChildRemoved"; + case 72: return "LayoutHint"; + case 73: return "ShowWindowRequest"; + case 74: return "WindowBlocked"; + case 75: return "WindowUnblocked"; + case 80: return "ActivateControl"; + case 81: return "DeactivateControl"; + case 82: return "ContextMenu"; + case 83: return "IMStart"; + case 84: return "IMCompose"; + case 85: return "IMEnd"; + case 86: return "Accessibility"; + case 87: return "TabletMove"; + case 88: return "LocaleChange"; + case 89: return "LanguageChange"; + case 90: return "LayoutDirectionChange"; + case 91: return "Style"; + case 92: return "TabletPress"; + case 93: return "TabletRelease"; + case 94: return "OkRequest"; + case 95: return "HelpRequest"; + case 96: return "WindowStateChange"; + case 97: return "IconDrag"; + case 1000: return "User"; + case 65535: return "MaxUser"; + default: return "Unknown"; + } +} +#endif + +// There are a number of events that we want to process. Not sure the +// ^Q thing is necessary (we have an action for this)? +bool RclMain::eventFilter( QObject * target, QEvent * event ) +{ +#if defined(SHOWEVENTS) + LOGDEB(("RclMain::eventFilter target %p, event %s\n", target, + eventTypeToStr(int(event->type())))); +#endif + if (event->type() == QEvent::KeyPress) { + QKeyEvent *keyEvent = (QKeyEvent *)event; + if (keyEvent->key() == Key_Q && (keyEvent->state() & ControlButton)) { + recollNeedsExit = 1; + } else if (keyEvent->key() == Key_Prior) { + resPageUpOrBack(); + return true; + } else if (keyEvent->key() == Key_Next) { + resPageDownOrNext(); + return true; + } + } else if (target == reslistTE->viewport()) { + // We don't want btdown+drag+btup to be a click ! So monitor + // mouse events + if (event->type() == QEvent::MouseMove) { + LOGDEB1(("reslistTE: MouseMove\n")); + if (reslist_mouseDown) + reslist_mouseDrag = true; + } else if (event->type() == QEvent::MouseButtonPress) { + LOGDEB1(("reslistTE: MouseButtonPress\n")); + reslist_mouseDown = true; + reslist_mouseDrag = false; + } else if (event->type() == QEvent::MouseButtonRelease) { + LOGDEB1(("reslistTE: MouseButtonRelease\n")); + reslist_mouseDown = false; + } else if (event->type() == QEvent::MouseButtonDblClick) { + LOGDEB1(("reslistTE: MouseButtonDblClick\n")); + reslist_mouseDown = false; + } + } + + return QWidget::eventFilter(target, event); +} + +void RclMain::fileExit() +{ + LOGDEB1(("RclMain: fileExit\n")); + if (asearchform) + delete asearchform; + // Let the exit handler clean up things + exit(0); +} + +// This is called on a 100ms timer checks the status of the indexing +// thread and a possible need to exit +void RclMain::periodic100() +{ + static int toggle; + // Check if indexing thread done + if (indexingstatus) { + statusBar()->message(""); + indexingstatus = false; + // Make sure we reopen the db to get the results. + LOGINFO(("Indexing done: closing query database\n")); + rcldb->close(); + } else if (indexingdone == 0) { + if (toggle < 9) { + statusBar()->message(tr("Indexing in progress")); + } else { + statusBar()->message(""); + } + if (toggle >= 10) + toggle = 0; + toggle++; + } + if (recollNeedsExit) + fileExit(); +} + +void RclMain::fileStart_IndexingAction_activated() +{ + if (indexingdone == 1) + startindexing = 1; +} + +// Note that all our 'urls' are like : file://... +static string urltolocalpath(string url) +{ + return url.substr(7, string::npos); +} + +// Translate paragraph number in list window to doc number. This depends on +// how we format the title etc.. +int RclMain::reldocnumfromparnum(int par) +{ + std::map::iterator it = pageParaToReldocnums.find(par); + int rdn; + if (it != pageParaToReldocnums.end()) { + rdn = it->second; + } else { + rdn = -1; + } + LOGDEB1(("reldocnumfromparnum: par %d reldoc %d\n", par, rdn)); + return rdn; +} + +// Double click in result list: use external viewer to display file +void RclMain::reslistTE_doubleClicked(int par, int) +{ + LOGDEB(("RclMain::reslistTE_doubleClicked: par %d\n", par)); + reslist_dblclck = true; + int reldocnum = reldocnumfromparnum(par); + if (reldocnum < 0) + return; + startNativeViewer(reslist_winfirst + reldocnum); +} + + +// Display preview for the selected document, and highlight entry. The +// paragraph number is doc number in window + 1 +// We don't actually do anything but start a timer because we want to +// check first if this might be a double click +void RclMain::reslistTE_clicked(int par, int car) +{ + if (reslist_waitingdbl) + return; + LOGDEB(("RclMain::reslistTE_clicked:wfirst %d par %d char %d drg %d\n", + reslist_winfirst, par, car, reslist_mouseDrag)); + if (reslist_winfirst == -1 || reslist_mouseDrag) + return; + + // remember par and car + reslist_par = par; + reslist_car = car; + reslist_waitingdbl = true; + reslist_dblclck = false; + // Wait to see if there's going to be a dblclck + QTimer::singleShot(150, this, SLOT(reslistTE_delayedclick()) ); +} + +// This gets called by a timer 100mS after a single click in the +// result list. We don't want to start a preview if the user has +// requested a native viewer by double-clicking +void RclMain::reslistTE_delayedclick() +{ + LOGDEB(("RclMain::reslistTE_delayedclick:\n")); + reslist_waitingdbl = false; + if (reslist_dblclck) { + LOGDEB1(("RclMain::reslistTE_delayedclick: dbleclick\n")); + reslist_dblclck = false; + return; + } + + int par = reslist_par; + + // Erase everything back to white + { + QColor color("white"); + for (int i = 1; i < reslistTE->paragraphs(); i++) + reslistTE->setParagraphBackgroundColor(i, color); + } + + // Color the new active paragraph + QColor color("lightblue"); + reslistTE->setParagraphBackgroundColor(par, color); + + // Document number + int reldocnum = reldocnumfromparnum(par); + + if (reldocnum < 0) { + // Bad number: must have clicked on header. Show details of query + QString desc = tr("Query details") + ": " + + QString::fromUtf8(currentQueryData.description.c_str()); + QMessageBox::information(this, tr("Query details"), desc); + return; + } else { + startPreview(reslist_winfirst + reldocnum); + } +} + +// User asked to start query. Send it to the db aand call +// listNextPB_clicked to fetch and display the first page of results +void RclMain::startSimpleSearch() +{ + LOGDEB(("RclMain::queryText_returnPressed()\n")); + // The db may have been closed at the end of indexing + Rcl::AdvSearchData sdata; + + QCString u8 = queryText->text().utf8(); + if (allTermsCB->isChecked()) + sdata.allwords = u8; + else + sdata.orwords = u8; + + startAdvSearch(sdata); +} + +// Execute an advanced search query. The parameters normally come from +// the advanced search dialog +void RclMain::startAdvSearch(Rcl::AdvSearchData sdata) +{ + LOGDEB(("RclMain::startAdvSearch\n")); + // The db may have been closed at the end of indexing + string reason; + if (!maybeOpenDb(reason)) { + QMessageBox::critical(0, "Recoll", QString(reason.c_str())); + exit(1); + } + + reslist_winfirst = -1; + + if (!rcldb->setQuery(sdata, prefs_queryStemLang.length() > 0 ? + Rcl::Db::QO_STEM : Rcl::Db::QO_NONE, + prefs_queryStemLang.ascii())) + return; + curPreview = 0; + if (docsource) + delete docsource; + + if (sortwidth > 0) { + DocSequenceDb myseq(rcldb, tr("Query results")); + docsource = new DocSeqSorted(myseq, sortwidth, sortspecs, + tr("Query results (sorted)")); + } else { + docsource = new DocSequenceDb(rcldb, tr("Query results")); + } + currentQueryData = sdata; + showResultPage(); +} + +// Page Up/Down: we don't try to check if current paragraph is last or +// first. We just page up/down and check if viewport moved. If it did, +// fair enough, else we go to next/previous result page. +void RclMain::resPageUpOrBack() +{ + int vpos = reslistTE->contentsY(); + reslistTE->moveCursor(QTextEdit::MovePgUp, false); + if (vpos == reslistTE->contentsY()) + resultPageBack(); +} +void RclMain::resPageDownOrNext() +{ + int vpos = reslistTE->contentsY(); + reslistTE->moveCursor(QTextEdit::MovePgDown, false); + LOGDEB(("RclMain::resPageDownOrNext: vpos before %d, after %d\n", + vpos, reslistTE->contentsY())); + if (vpos == reslistTE->contentsY()) + showResultPage(); +} + +// Show previous page of results. We just set the current number back +// 2 pages and show next page. +void RclMain::resultPageBack() +{ + if (reslist_winfirst <= 0) + return; + reslist_winfirst -= 2 * prefs_respagesize; + showResultPage(); +} + + +// Fill up result list window with next screen of hits +void RclMain::showResultPage() +{ + if (!docsource) + return; + + int percent; + Rcl::Doc doc; + + int resCnt = docsource->getResCnt(); + + LOGDEB(("showResultPage: rescnt %d, winfirst %d\n", resCnt, + reslist_winfirst)); + + pageParaToReldocnums.clear(); + + // If we are already on the last page, nothing to do: + if (reslist_winfirst >= 0 && + (reslist_winfirst + prefs_respagesize > resCnt)) { + nextPageAction->setEnabled(false); + return; + } + + if (reslist_winfirst < 0) { + reslist_winfirst = 0; + prevPageAction->setEnabled(false); + } else { + prevPageAction->setEnabled(true); + reslist_winfirst += prefs_respagesize; + } + + bool gotone = false; + reslistTE->clear(); + + int last = MIN(resCnt-reslist_winfirst, prefs_respagesize); + + + // Insert results if any in result list window. We have to send + // the text to the widgets, because we need the paragraph number + // each time we add a result paragraph (its diffult and + // error-prone to compute the paragraph numbers in parallel. We + // would like to disable updates while we're doing this, but + // couldn't find a way to make it work, the widget seems to become + // confused if appended while updates are disabled + // reslistTE->setUpdatesEnabled(false); + for (int i = 0; i < last; i++) { + string sh; + doc.erase(); + + if (!docsource->getDoc(reslist_winfirst + i, doc, &percent, &sh)) { + // This may very well happen for history if the doc has + // been removed since. So don't treat it as fatal. + doc.abstract = string(tr("Unavailable document").utf8()); + } + if (i == 0) { + // Display header + // We could use a but the textedit doesnt display + // it prominently + reslistTE->append("<qt><head></head><body>"); + QString line = "<p><font size=+1><b>"; + line += docsource->title().c_str(); + line += "</b></font><br>"; + reslistTE->append(line); + line = tr("<b>Displaying results starting at index" + " %1 (maximum set size %2)</b></p>\n") + .arg(reslist_winfirst+1) + .arg(resCnt); + reslistTE->append(line); + } + + gotone = true; + + // Result list entry display: this must be exactly one paragraph + // TOBEDONE + // - move abstract/keywords to Detail window ? + // - keywords matched ? + // - language ? + // - size ? + + string result; + if (!sh.empty()) + result += string("<p><b>") + sh + "</p>\n<p>"; + else + result = "<p>"; + + string img_name; + if (prefs_showicons) { + string iconname = rclconfig->getMimeIconName(doc.mimetype); + if (iconname.empty()) + iconname = "document"; + string imgfile = iconsdir + "/" + iconname + ".png"; + + LOGDEB1(("Img file; %s\n", imgfile.c_str())); + QImage image(imgfile.c_str()); + if (!image.isNull()) { + img_name = string("img_") + iconname; + QMimeSourceFactory::defaultFactory()-> + setImage(img_name.c_str(), image); + } + } + + char perbuf[10]; + sprintf(perbuf, "%3d%%", percent); + if (doc.title.empty()) + doc.title = path_getsimple(doc.url); + char datebuf[100]; + datebuf[0] = 0; + if (!doc.dmtime.empty() || !doc.fmtime.empty()) { + time_t mtime = doc.dmtime.empty() ? + atol(doc.fmtime.c_str()) : atol(doc.dmtime.c_str()); + struct tm *tm = localtime(&mtime); + strftime(datebuf, 99, + "<i>Modified:</i> %Y-%m-%d %H:%M:%S", tm); + } + string abst = escapeHtml(doc.abstract); + LOGDEB1(("Abstract: {%s}\n", abst.c_str())); + if (!img_name.empty()) { + result += "<img source=\"" + img_name + "\" align=\"left\">"; + } + result += string(perbuf) + " <b>" + doc.title + "</b><br>" + + doc.mimetype + " " + + (datebuf[0] ? string(datebuf) + "<br>" : string("<br>")) + + (!abst.empty() ? abst + "<br>" : string("")) + + (!doc.keywords.empty() ? doc.keywords + "<br>" : string("")) + + "<i>" + doc.url + +"</i><br></p>\n"; + + QString str = QString::fromUtf8(result.c_str(), result.length()); + reslistTE->append(str); + + pageParaToReldocnums[reslistTE->paragraphs()-1] = i; + } + + if (gotone) { + reslistTE->append("</body></qt>"); + reslistTE->setCursorPosition(0,0); + reslistTE->ensureCursorVisible(); + } else { + // Restore first in win parameter that we shouln't have incremented + reslistTE->append(tr("<p>" + /*"<img align=\"left\" source=\"myimage\">"*/ + "<b>No results found</b>" + "<br>")); + reslist_winfirst -= prefs_respagesize; + if (reslist_winfirst < 0) + reslist_winfirst = -1; + } + + //reslistTE->setUpdatesEnabled(true);reslistTE->sync();reslistTE->repaint(); + +#if 0 + { + FILE *fp = fopen("/tmp/reslistdebug", "w"); + if (fp) { + const char *text = (const char *)reslistTE->text().utf8(); + //const char *text = alltext.c_str(); + fwrite(text, 1, strlen(text), fp); + fclose(fp); + } + } +#endif + + if (reslist_winfirst < 0 || + (reslist_winfirst >= 0 && + reslist_winfirst + prefs_respagesize >= resCnt)) { + nextPageAction->setEnabled(false); + } else { + nextPageAction->setEnabled(true); + } +} + +// 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(QWidget *w) +{ + if (w == (QWidget *)curPreview) { + LOGDEB(("Active preview closed\n")); + curPreview = 0; + } else { + LOGDEB(("Old preview closed\n")); + } + delete w; +} + +// Open advanced search dialog. +void RclMain::showAdvSearchDialog() +{ + if (asearchform == 0) { + asearchform = new advsearch(0, tr("Advanced search"), FALSE, + WStyle_Customize | WStyle_NormalBorder | + WStyle_Title | WStyle_SysMenu); + asearchform->setSizeGripEnabled(FALSE); + connect(asearchform, SIGNAL(startSearch(Rcl::AdvSearchData)), + this, SLOT(startAdvSearch(Rcl::AdvSearchData))); + asearchform->show(); + } else { + // Close and reopen, in hope that makes us visible... + asearchform->close(); + asearchform->show(); + } +} + +void RclMain::showSortDialog() +{ + if (sortform == 0) { + sortform = new SortForm(0, tr("Sort criteria"), FALSE, + WStyle_Customize | WStyle_NormalBorder | + WStyle_Title | WStyle_SysMenu); + sortform->setSizeGripEnabled(FALSE); + connect(sortform, SIGNAL(sortDataChanged(int, RclSortSpec)), + this, SLOT(sortDataChanged(int, RclSortSpec))); + sortform->show(); + } else { + // Close and reopen, in hope that makes us visible... + sortform->close(); + sortform->show(); + } + +} + +void RclMain::showUIPrefs() +{ + if (uiprefs == 0) { + uiprefs = new UIPrefsDialog(0, tr("User interface preferences"), FALSE, + WStyle_Customize | WStyle_NormalBorder | + WStyle_Title | WStyle_SysMenu); + uiprefs->setSizeGripEnabled(FALSE); + connect(uiprefs, SIGNAL(uiprefsDone()), this, SLOT(setUIPrefs())); + uiprefs->show(); + } else { + // Close and reopen, in hope that makes us visible... + uiprefs->close(); + uiprefs->show(); + } +} + +/** + * Open a preview window for a given document, or load it into new tab of + * existing window. + * + * @param docnum db query index + */ +void RclMain::startPreview(int docnum) +{ + Rcl::Doc doc; + if (!docsource->getDoc(docnum, doc, 0)) { + QMessageBox::warning(0, "Recoll", + tr("Cannot retrieve document info" + " from database")); + return; + } + + // Check file exists in file system + string fn = urltolocalpath(doc.url); + struct stat st; + if (stat(fn.c_str(), &st) < 0) { + QMessageBox::warning(0, "Recoll", tr("Cannot access document file: ") + + fn.c_str()); + return; + } + + if (curPreview == 0) { + curPreview = new Preview(0, tr("Preview")); + if (curPreview == 0) { + QMessageBox::warning(0, tr("Warning"), + tr("Can't create preview window"), + QMessageBox::Ok, + QMessageBox::NoButton); + return; + } + + curPreview->setCaption(queryText->text()); + connect(curPreview, SIGNAL(previewClosed(Widget *)), + this, SLOT(previewClosed(Widget *))); + curPreview->show(); + } else { + if (curPreview->makeDocCurrent(fn, doc)) { + // Already there + return; + } + (void)curPreview->addEditorTab(); + } + history->enterDocument(fn, doc.ipath); + curPreview->loadFileInCurrentTab(fn, st.st_size, doc); +} + +void RclMain::startNativeViewer(int docnum) +{ + Rcl::Doc doc; + if (!docsource->getDoc(docnum, doc, 0, 0)) { + QMessageBox::warning(0, "Recoll", + tr("Cannot retrieve document info" + " from database")); + return; + } + + // Look for appropriate viewer + string cmd = rclconfig->getMimeViewerDef(doc.mimetype); + if (cmd.length() == 0) { + QMessageBox::warning(0, "Recoll", + tr("No external viewer configured for mime type ") + + doc.mimetype.c_str()); + return; + } + + string fn = urltolocalpath(doc.url); + + // Substitute %u (url) and %f (file name) inside prototype command + string ncmd; + string::const_iterator it1; + for (it1 = cmd.begin(); it1 != cmd.end();it1++) { + if (*it1 == '%') { + if (++it1 == cmd.end()) { + ncmd += '%'; + break; + } + if (*it1 == '%') + ncmd += '%'; + if (*it1 == 'u') + ncmd += "'" + doc.url + "'"; + if (*it1 == 'f') + ncmd += "'" + fn + "'"; + } else { + ncmd += *it1; + } + } + + ncmd += " &"; + QStatusBar *stb = statusBar(); + if (stb) { + QString msg = tr("Executing: [") + ncmd.c_str() + "]"; + stb->message(msg, 5000); + stb->repaint(false); + XFlush(qt_xdisplay()); + } + history->enterDocument(fn, doc.ipath); + system(ncmd.c_str()); +} + + +void RclMain::showAboutDialog() +{ + string vstring = string("Recoll ") + rclversion + + "<br>" + "http://www.recoll.org"; + QMessageBox::information(this, tr("About Recoll"), vstring.c_str()); +} + +void RclMain::startManual() +{ + startHelpBrowser(); +} + +void RclMain::showDocHistory() +{ + LOGDEB(("RclMain::showDocHistory\n")); + reslist_winfirst = -1; + curPreview = 0; + if (docsource) + delete docsource; + + if (sortwidth > 0) { + DocSequenceHistory myseq(rcldb, history, tr("Document history")); + docsource = new DocSeqSorted(myseq, sortwidth, sortspecs, + tr("Document history (sorted)")); + } else { + docsource = new DocSequenceHistory(rcldb, history, + tr("Document history")); + } + currentQueryData.erase(); + currentQueryData.description = tr("History data").utf8(); + showResultPage(); +} + + +void RclMain::searchTextChanged(const QString & text) +{ + if (text.isEmpty()) { + searchPB->setEnabled(false); + clearqPB->setEnabled(false); + } else { + searchPB->setEnabled(true); + clearqPB->setEnabled(true); + } + +} + +void RclMain::sortDataChanged(int cnt, RclSortSpec spec) +{ + LOGDEB(("RclMain::sortDataChanged\n")); + sortwidth = cnt; + sortspecs = spec; +} + +// This could be handled inside the dialog's accept(), but we may want to +// do something (ie: redisplay reslist?) +void RclMain::setUIPrefs() +{ + if (!uiprefs) + return; + LOGDEB(("Recollmain::setUIPrefs\n")); + prefs_showicons = uiprefs->useIconsCB->isChecked(); + prefs_respagesize = uiprefs->pageLenSB->value(); + + prefs_reslistfontfamily = uiprefs->reslistFontFamily; + prefs_reslistfontsize = uiprefs->reslistFontSize; + if (prefs_reslistfontfamily.length()) { + QFont nfont(prefs_reslistfontfamily, prefs_reslistfontsize); + reslistTE->setFont(nfont); + } else { + reslistTE->setFont(this->font()); + } + + if (uiprefs->stemLangCMB->currentItem() == 0) { + prefs_queryStemLang = ""; + } else { + prefs_queryStemLang = uiprefs->stemLangCMB->currentText(); + } +} diff --git a/src/qtgui/rclmain.h b/src/qtgui/rclmain.h new file mode 100644 index 00000000..11a92b89 --- /dev/null +++ b/src/qtgui/rclmain.h @@ -0,0 +1,80 @@ +#ifndef RCLMAIN_H +#define RCLMAIN_H + +#include <qvariant.h> +#include <qmainwindow.h> +#include "sortseq.h" +#include "preview.h" +#include "recoll.h" +#include "advsearch.h" +#include "sort.h" +#include "uiprefs.h" +#include "rcldb.h" + +#include "recollmain.h" + +class RclMain : public RclMainBase +{ + Q_OBJECT + +public: + RclMain(QWidget* parent = 0, const char* name = 0, + WFlags fl = WType_TopLevel) + : RclMainBase(parent,name,fl) { + init(); + } + ~RclMain() {} + + virtual bool close( bool ); + +public slots: + virtual void fileExit(); + virtual void periodic100(); + virtual void fileStart_IndexingAction_activated(); + virtual void reslistTE_doubleClicked( int par, int ); + virtual void reslistTE_clicked( int par, int car ); + virtual void reslistTE_delayedclick(); + virtual void startSimpleSearch(); + virtual void startAdvSearch( Rcl::AdvSearchData sdata ); + virtual void resPageUpOrBack(); + virtual void resPageDownOrNext(); + virtual void resultPageBack(); + virtual void showResultPage(); + virtual void previewClosed( QWidget * w ); + virtual void showAdvSearchDialog(); + virtual void showSortDialog(); + virtual void showAboutDialog(); + virtual void startManual(); + virtual void showDocHistory(); + virtual void searchTextChanged( const QString & text ); + virtual void sortDataChanged( int cnt, RclSortSpec spec ); + virtual void showUIPrefs(); + virtual void setUIPrefs(); + +protected: + int reslist_winfirst; + bool reslist_mouseDrag; + bool reslist_mouseDown; + int reslist_par; + int reslist_car; + bool reslist_waitingdbl; + bool reslist_dblclck; + Preview *curPreview; + advsearch *asearchform; + Rcl::AdvSearchData currentQueryData; + SortForm *sortform; + UIPrefsDialog *uiprefs; + int sortwidth; + RclSortSpec sortspecs; + DocSequence *docsource; + std::map<int,int> pageParaToReldocnums; + +private: + virtual void init(); + virtual bool eventFilter( QObject * target, QEvent * event ); + virtual int reldocnumfromparnum( int par ); + virtual void startPreview( int docnum ); + virtual void startNativeViewer( int docnum ); +}; + +#endif // RCLMAIN_H diff --git a/src/qtgui/recoll.pro b/src/qtgui/recoll.pro index d9bfaaf9..9cb486b1 100644 --- a/src/qtgui/recoll.pro +++ b/src/qtgui/recoll.pro @@ -4,14 +4,18 @@ LANGUAGE = C++ CONFIG += qt warn_on thread release debug SOURCES += main.cpp \ + rclmain.cpp \ idxthread.cpp \ plaintorich.cpp +HEADERS = rclmain.h + FORMS = recollmain.ui \ - advsearch.ui \ - preview/preview.ui \ - sort.ui \ - uiprefs.ui + advsearch.ui \ + preview/preview.ui \ + sort.ui \ + uiprefs.ui + IMAGES = images/filenew \ images/fileopen \ diff --git a/src/qtgui/recoll_fr.ts b/src/qtgui/recoll_fr.ts index 02e9ff98..3afc4d03 100644 --- a/src/qtgui/recoll_fr.ts +++ b/src/qtgui/recoll_fr.ts @@ -17,10 +17,6 @@ <source>Could not open database in </source> <translation>Impossible d'ouvrir la base dans</translation> </message> - <message> - <source>. Starting indexation</source> - <translation type="obsolete">. Démarrage de l'indexation</translation> - </message> <message> <source>. Click Cancel if you want to edit the configuration file before indexation starts, or Ok to let it proceed.</source> @@ -65,10 +61,6 @@ Click Cancel if you want to edit the configuration file before indexation starts <source>Alt+C</source> <translation>Alt+C</translation> </message> - <message> - <source>Close Tab</source> - <translation type="obsolete">Fermer la tabulation</translation> - </message> <message> <source>Clear</source> <translation>Effacer</translation> @@ -87,75 +79,15 @@ Click Cancel if you want to edit the configuration file before indexation starts </message> </context> <context> - <name>RecollMain</name> - <message> - <source>recoll</source> - <translation type="obsolete">recoll</translation> - </message> - <message> - <source>Search</source> - <translation>Rechercher</translation> - </message> - <message> - <source>Clear</source> - <translation>Effacer</translation> - </message> - <message> - <source>Ctrl+S</source> - <translation>Ctrl+S</translation> - </message> - <message> - <source>Previous page</source> - <translation>Page précedente</translation> - </message> - <message> - <source>Next page</source> - <translation>Page suivante</translation> - </message> + <name>RclMain</name> <message> <source>Advanced search</source> <translation>Recherche avancée</translation> </message> - <message> - <source>&File</source> - <translation>&Fichier</translation> - </message> - <message> - <source>Exit</source> - <translation>Quitter</translation> - </message> - <message> - <source>E&xit</source> - <translation>&Quitter</translation> - </message> - <message> - <source>Start Indexing</source> - <translation>Commencer l'indexation</translation> - </message> - <message> - <source>Help</source> - <translation type="obsolete">Aide</translation> - </message> <message> <source>About Recoll</source> <translation>À propos de Recoll</translation> </message> - <message> - <source>&Tools</source> - <translation>&Outils</translation> - </message> - <message> - <source>&Help</source> - <translation>&Aide</translation> - </message> - <message> - <source>Ctrl+Q</source> - <translation type="obsolete">Ctrl+Q</translation> - </message> - <message> - <source>Doc History</source> - <translation type="obsolete">Historique documents</translation> - </message> <message> <source>Indexing in progress</source> <translation>Indexation en cours</translation> @@ -168,10 +100,6 @@ Click Cancel if you want to edit the configuration file before indexation starts <source>Executing: [</source> <translation>Exécution de: [</translation> </message> - <message> - <source><b>Displaying results starting at index %1 (maximum set size %2)</b></p></source> - <translation type="obsolete"><b>Affichage des résultats à partir de l'indice %1 (taille maximum estimée %2)</b></p></translation> - </message> <message> <source>Cannot retrieve document info from database</source> <translation>Impossible d'accéder au document dans la base</translation> @@ -193,8 +121,93 @@ Click Cancel if you want to edit the configuration file before indexation starts <translation>Impossible de créer la fenetre de visualisation</translation> </message> <message> - <source>Recoll</source> - <translation></translation> + <source><b>Displaying results starting at index %1 (maximum set size %2)</b></p> +</source> + <translation><b>Affichage des résultats à partir de l'index %1 (nombre maximum %2)</b></p> +</translation> + </message> + <message> + <source>Sort criteria</source> + <translation>Critères de tri</translation> + </message> + <message> + <source>User interface preferences</source> + <translation>Préférences pour l'interface utilisateur</translation> + </message> + <message> + <source>Query results</source> + <translation>Résultats de la recherche</translation> + </message> + <message> + <source>Query results (sorted)</source> + <translation>Résultats de la recherche (triés)</translation> + </message> + <message> + <source>Document history</source> + <translation>Historique des documents consultés</translation> + </message> + <message> + <source>Document history (sorted)</source> + <translation>Historique des documents consultés (trié)</translation> + </message> + <message> + <source>Query details</source> + <translation>Détail de la recherche</translation> + </message> + <message> + <source>Unavailable document</source> + <translation>Document inaccessible</translation> + </message> + <message> + <source>History data</source> + <translation>Données d'historique</translation> + </message> +</context> +<context> + <name>RclMainBase</name> + <message> + <source>Search</source> + <translation>Rechercher</translation> + </message> + <message> + <source>Clear</source> + <translation>Effacer</translation> + </message> + <message> + <source>Ctrl+S</source> + <translation>Ctrl+S</translation> + </message> + <message> + <source>Previous page</source> + <translation>Page précedente</translation> + </message> + <message> + <source>Next page</source> + <translation>Page suivante</translation> + </message> + <message> + <source>&File</source> + <translation>&Fichier</translation> + </message> + <message> + <source>Exit</source> + <translation>Quitter</translation> + </message> + <message> + <source>E&xit</source> + <translation>&Quitter</translation> + </message> + <message> + <source>Start Indexing</source> + <translation>Commencer l'indexation</translation> + </message> + <message> + <source>&Tools</source> + <translation>&Outils</translation> + </message> + <message> + <source>&Help</source> + <translation>&Aide</translation> </message> <message> <source>Erase search entry</source> @@ -236,10 +249,6 @@ Click Cancel if you want to edit the configuration file before indexation starts <source>Result list</source> <translation>Liste de résultats</translation> </message> - <message> - <source>Toolbar_2</source> - <translation type="obsolete">Toolbar_2</translation> - </message> <message> <source>Start &Indexing</source> <translation>Démarrer l'&Indexation</translation> @@ -288,20 +297,6 @@ Click Cancel if you want to edit the configuration file before indexation starts <source>&Query configuration</source> <translation>Configuration pour la &recherche</translation> </message> - <message> - <source><b>Displaying results starting at index %1 (maximum set size %2)</b></p> -</source> - <translation><b>Affichage des résultats à partir de l'index %1 (nombre maximum %2)</b></p> -</translation> - </message> - <message> - <source>Sort criteria</source> - <translation>Critères de tri</translation> - </message> - <message> - <source>User interface preferences</source> - <translation>Préférences pour l'interface utilisateur</translation> - </message> <message> <source>User manual</source> <translation>Manuel</translation> @@ -311,32 +306,12 @@ Click Cancel if you want to edit the configuration file before indexation starts <translation>&Manuel</translation> </message> <message> - <source>Query results</source> - <translation>Résultats de la recherche</translation> + <source>Recoll</source> + <translation>Recoll</translation> </message> <message> - <source>Query results (sorted)</source> - <translation>Résultats de la recherche (triés)</translation> - </message> - <message> - <source>Document history</source> - <translation>Historique des documents consultés</translation> - </message> - <message> - <source>Document history (sorted)</source> - <translation>Historique des documents consultés (trié)</translation> - </message> - <message> - <source>Query details</source> - <translation>Détail de la recherche</translation> - </message> - <message> - <source>Unavailable document</source> - <translation>Document inaccessible</translation> - </message> - <message> - <source>History data</source> - <translation>Données d'historique</translation> + <source>About Recoll</source> + <translation>À propos de Recoll</translation> </message> </context> <context> @@ -463,14 +438,6 @@ Click Cancel if you want to edit the configuration file before indexation starts <source>Searched file types</source> <translation>Type de fichier recherché</translation> </message> - <message> - <source>--------></source> - <translation type="obsolete">--------></translation> - </message> - <message> - <source><---------</source> - <translation type="obsolete"><---------</translation> - </message> <message> <source>Ignored file types</source> <translation>Types de fichier ignorés</translation> diff --git a/src/qtgui/recollmain.ui b/src/qtgui/recollmain.ui index 95a70db9..35e8928e 100644 --- a/src/qtgui/recollmain.ui +++ b/src/qtgui/recollmain.ui @@ -1,8 +1,8 @@ <!DOCTYPE UI><UI version="3.3" stdsetdef="1"> -<class>RecollMain</class> +<class>RclMainBase</class> <widget class="QMainWindow"> <property name="name"> - <cstring>RecollMain</cstring> + <cstring>RclMainBase</cstring> </property> <property name="geometry"> <rect> @@ -346,61 +346,61 @@ <connection> <sender>fileExitAction</sender> <signal>activated()</signal> - <receiver>RecollMain</receiver> + <receiver>RclMainBase</receiver> <slot>fileExit()</slot> </connection> <connection> <sender>fileStart_IndexingAction</sender> <signal>activated()</signal> - <receiver>RecollMain</receiver> + <receiver>RclMainBase</receiver> <slot>fileStart_IndexingAction_activated()</slot> </connection> <connection> <sender>searchPB</sender> <signal>clicked()</signal> - <receiver>RecollMain</receiver> + <receiver>RclMainBase</receiver> <slot>startSimpleSearch()</slot> </connection> <connection> <sender>queryText</sender> <signal>returnPressed()</signal> - <receiver>RecollMain</receiver> + <receiver>RclMainBase</receiver> <slot>startSimpleSearch()</slot> </connection> <connection> <sender>reslistTE</sender> <signal>doubleClicked(int,int)</signal> - <receiver>RecollMain</receiver> + <receiver>RclMainBase</receiver> <slot>reslistTE_doubleClicked(int,int)</slot> </connection> <connection> <sender>reslistTE</sender> <signal>clicked(int,int)</signal> - <receiver>RecollMain</receiver> + <receiver>RclMainBase</receiver> <slot>reslistTE_clicked(int,int)</slot> </connection> <connection> <sender>helpAbout_RecollAction</sender> <signal>activated()</signal> - <receiver>RecollMain</receiver> + <receiver>RclMainBase</receiver> <slot>showAboutDialog()</slot> </connection> <connection> <sender>userManualAction</sender> <signal>activated()</signal> - <receiver>RecollMain</receiver> + <receiver>RclMainBase</receiver> <slot>startManual()</slot> </connection> <connection> <sender>toolsDoc_HistoryAction</sender> <signal>activated()</signal> - <receiver>RecollMain</receiver> + <receiver>RclMainBase</receiver> <slot>showDocHistory()</slot> </connection> <connection> <sender>queryText</sender> <signal>textChanged(const QString&)</signal> - <receiver>RecollMain</receiver> + <receiver>RclMainBase</receiver> <slot>searchTextChanged(const QString&)</slot> </connection> <connection> @@ -412,61 +412,37 @@ <connection> <sender>toolsAdvanced_SearchAction</sender> <signal>activated()</signal> - <receiver>RecollMain</receiver> + <receiver>RclMainBase</receiver> <slot>showAdvSearchDialog()</slot> </connection> <connection> <sender>toolsSort_parametersAction</sender> <signal>activated()</signal> - <receiver>RecollMain</receiver> + <receiver>RclMainBase</receiver> <slot>showSortDialog()</slot> </connection> <connection> <sender>prevPageAction</sender> <signal>activated()</signal> - <receiver>RecollMain</receiver> + <receiver>RclMainBase</receiver> <slot>resultPageBack()</slot> </connection> <connection> <sender>nextPageAction</sender> <signal>activated()</signal> - <receiver>RecollMain</receiver> + <receiver>RclMainBase</receiver> <slot>showResultPage()</slot> </connection> <connection> <sender>preferencesQuery_PrefsAction</sender> <signal>activated()</signal> - <receiver>RecollMain</receiver> + <receiver>RclMainBase</receiver> <slot>showUIPrefs()</slot> </connection> </connections> <includes> - <include location="local" impldecl="in declaration">sortseq.h</include> - <include location="local" impldecl="in declaration">preview.h</include> - <include location="local" impldecl="in declaration">recoll.h</include> - <include location="local" impldecl="in declaration">advsearch.h</include> - <include location="local" impldecl="in declaration">sort.h</include> - <include location="local" impldecl="in declaration">uiprefs.h</include> - <include location="local" impldecl="in declaration">rcldb.h</include> - <include location="local" impldecl="in implementation">recollmain.ui.h</include> </includes> <variables> - <variable>int reslist_winfirst;</variable> - <variable>bool reslist_mouseDrag;</variable> - <variable>bool reslist_mouseDown;</variable> - <variable>int reslist_par;</variable> - <variable>int reslist_car;</variable> - <variable>bool reslist_waitingdbl;</variable> - <variable>bool reslist_dblclck;</variable> - <variable>Preview *curPreview;</variable> - <variable>advsearch *asearchform;</variable> - <variable>Rcl::AdvSearchData currentQueryData;</variable> - <variable>SortForm *sortform;</variable> - <variable>UIPrefsDialog *uiprefs;</variable> - <variable>int sortwidth;</variable> - <variable>RclSortSpec sortspecs;</variable> - <variable>DocSequence *docsource;</variable> - <variable>std::map<int,int> pageParaToReldocnums;</variable> </variables> <slots> <slot>fileExit()</slot> @@ -476,29 +452,21 @@ <slot>reslistTE_clicked( int par, int car )</slot> <slot>reslistTE_delayedclick()</slot> <slot>startSimpleSearch()</slot> - <slot>startAdvSearch( Rcl::AdvSearchData sdata )</slot> <slot>resPageUpOrBack()</slot> <slot>resPageDownOrNext()</slot> <slot>resultPageBack()</slot> <slot>showResultPage()</slot> - <slot>previewClosed( Preview * w )</slot> + <slot>previewClosed( QWidget * w )</slot> <slot>showAdvSearchDialog()</slot> <slot>showSortDialog()</slot> <slot>showAboutDialog()</slot> <slot>startManual()</slot> <slot>showDocHistory()</slot> <slot>searchTextChanged( const QString & text )</slot> - <slot>sortDataChanged( int cnt, RclSortSpec spec )</slot> <slot>showUIPrefs()</slot> <slot>setUIPrefs()</slot> </slots> <functions> - <function access="private">init()</function> - <function returnType="bool">close( bool )</function> - <function access="private" returnType="bool">eventFilter( QObject * target, QEvent * event )</function> - <function access="private" returnType="int">reldocnumfromparnum( int par )</function> - <function access="private">startPreview( int docnum )</function> - <function access="private">startNativeViewer( int docnum )</function> </functions> <pixmapinproject/> <layoutdefaults spacing="6" margin="11"/> diff --git a/src/qtgui/recollmain.ui.h b/src/qtgui/recollmain.ui.h index efa82d13..18b1069c 100644 --- a/src/qtgui/recollmain.ui.h +++ b/src/qtgui/recollmain.ui.h @@ -9,848 +9,63 @@ ** These will automatically be called by the form's constructor and ** destructor. *****************************************************************************/ - -#include <stdlib.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <unistd.h> -#include <fcntl.h> -#include <regex.h> - -#include <utility> -#ifndef NO_NAMESPACES -using std::pair; -#endif /* NO_NAMESPACES */ - -#include <qmessagebox.h> -#include <qcstring.h> -#include <qtabwidget.h> -#include <qtimer.h> -#include <qstatusbar.h> -#include <qwindowdefs.h> -#include <qapplication.h> -#include <qcheckbox.h> -#include <qfontdialog.h> -#include <qspinbox.h> -#include <qcombobox.h> - -#include "recoll.h" -#include "debuglog.h" -#include "mimehandler.h" -#include "pathut.h" -#include "smallut.h" -#include "plaintorich.h" -#include "advsearch.h" -#include "rclversion.h" -#include "sortseq.h" -#include "uiprefs.h" - -extern "C" int XFlush(void *); - -#ifndef MIN -#define MIN(A,B) ((A) < (B) ? (A) : (B)) -#endif - -void RecollMain::init() +void RclMainBase::fileExit() { - reslist_winfirst = -1; - reslist_mouseDrag = false; - reslist_mouseDown = false; - reslist_par = -1; - reslist_car = -1; - reslist_waitingdbl = false; - reslist_dblclck = false; - curPreview = 0; - asearchform = 0; - sortform = 0; - docsource = 0; - sortwidth = 0; - uiprefs = 0; - - // We manage pgup/down, but let ie the arrows for the editor to process - reslistTE->installEventFilter(this); - reslistTE->viewport()->installEventFilter(this); - // reslistTE->viewport()->setFocusPolicy(QWidget::NoFocus); - - // Set the focus to the search terms entry: - queryText->setFocus(); - - // Set result list font according to user preferences. - if (prefs_reslistfontfamily.length()) { - QFont nfont(prefs_reslistfontfamily, prefs_reslistfontsize); - reslistTE->setFont(nfont); - } } - -// We also want to get rid of the advanced search form and previews -// when we exit (not our children so that it's not systematically -// created over the main form). -bool RecollMain::close(bool) +void RclMainBase::periodic100() { - fileExit(); - return false; } - -//#define SHOWEVENTS -#if defined(SHOWEVENTS) -static const char *eventTypeToStr(int tp) +void RclMainBase::fileStart_IndexingAction_activated() { - switch (tp) { - case 0: return "None"; - case 1: return "Timer"; - case 2: return "MouseButtonPress"; - case 3: return "MouseButtonRelease"; - case 4: return "MouseButtonDblClick"; - case 5: return "MouseMove"; - case 6: return "KeyPress"; - case 7: return "KeyRelease"; - case 8: return "FocusIn"; - case 9: return "FocusOut"; - case 10: return "Enter"; - case 11: return "Leave"; - case 12: return "Paint"; - case 13: return "Move"; - case 14: return "Resize"; - case 15: return "Create"; - case 16: return "Destroy"; - case 17: return "Show"; - case 18: return "Hide"; - case 19: return "Close"; - case 20: return "Quit"; - case 21: return "Reparent"; - case 22: return "ShowMinimized"; - case 23: return "ShowNormal"; - case 24: return "WindowActivate"; - case 25: return "WindowDeactivate"; - case 26: return "ShowToParent"; - case 27: return "HideToParent"; - case 28: return "ShowMaximized"; - case 29: return "ShowFullScreen"; - case 30: return "Accel"; - case 31: return "Wheel"; - case 32: return "AccelAvailable"; - case 33: return "CaptionChange"; - case 34: return "IconChange"; - case 35: return "ParentFontChange"; - case 36: return "ApplicationFontChange"; - case 37: return "ParentPaletteChange"; - case 38: return "ApplicationPaletteChange"; - case 39: return "PaletteChange"; - case 40: return "Clipboard"; - case 42: return "Speech"; - case 50: return "SockAct"; - case 51: return "AccelOverride"; - case 52: return "DeferredDelete"; - case 60: return "DragEnter"; - case 61: return "DragMove"; - case 62: return "DragLeave"; - case 63: return "Drop"; - case 64: return "DragResponse"; - case 70: return "ChildInserted"; - case 71: return "ChildRemoved"; - case 72: return "LayoutHint"; - case 73: return "ShowWindowRequest"; - case 74: return "WindowBlocked"; - case 75: return "WindowUnblocked"; - case 80: return "ActivateControl"; - case 81: return "DeactivateControl"; - case 82: return "ContextMenu"; - case 83: return "IMStart"; - case 84: return "IMCompose"; - case 85: return "IMEnd"; - case 86: return "Accessibility"; - case 87: return "TabletMove"; - case 88: return "LocaleChange"; - case 89: return "LanguageChange"; - case 90: return "LayoutDirectionChange"; - case 91: return "Style"; - case 92: return "TabletPress"; - case 93: return "TabletRelease"; - case 94: return "OkRequest"; - case 95: return "HelpRequest"; - case 96: return "WindowStateChange"; - case 97: return "IconDrag"; - case 1000: return "User"; - case 65535: return "MaxUser"; - default: return "Unknown"; - } } -#endif - -// There are a number of events that we want to process. Not sure the -// ^Q thing is necessary (we have an action for this)? -bool RecollMain::eventFilter( QObject * target, QEvent * event ) +void RclMainBase::reslistTE_doubleClicked( int , int ) { -#if defined(SHOWEVENTS) - LOGDEB(("RecollMain::eventFilter target %p, event %s\n", target, - eventTypeToStr(int(event->type())))); -#endif - if (event->type() == QEvent::KeyPress) { - QKeyEvent *keyEvent = (QKeyEvent *)event; - if (keyEvent->key() == Key_Q && (keyEvent->state() & ControlButton)) { - recollNeedsExit = 1; - } else if (keyEvent->key() == Key_Prior) { - resPageUpOrBack(); - return true; - } else if (keyEvent->key() == Key_Next) { - resPageDownOrNext(); - return true; - } - } else if (target == reslistTE->viewport()) { - // We don't want btdown+drag+btup to be a click ! So monitor - // mouse events - if (event->type() == QEvent::MouseMove) { - LOGDEB1(("reslistTE: MouseMove\n")); - if (reslist_mouseDown) - reslist_mouseDrag = true; - } else if (event->type() == QEvent::MouseButtonPress) { - LOGDEB1(("reslistTE: MouseButtonPress\n")); - reslist_mouseDown = true; - reslist_mouseDrag = false; - } else if (event->type() == QEvent::MouseButtonRelease) { - LOGDEB1(("reslistTE: MouseButtonRelease\n")); - reslist_mouseDown = false; - } else if (event->type() == QEvent::MouseButtonDblClick) { - LOGDEB1(("reslistTE: MouseButtonDblClick\n")); - reslist_mouseDown = false; - } - } - - return QWidget::eventFilter(target, event); } - -void RecollMain::fileExit() +void RclMainBase::reslistTE_clicked( int , int ) { - LOGDEB1(("RecollMain: fileExit\n")); - if (asearchform) - delete asearchform; - // Let the exit handler clean up things - exit(0); } - -// This is called on a 100ms timer checks the status of the indexing -// thread and a possible need to exit -void RecollMain::periodic100() +void RclMainBase::reslistTE_delayedclick() { - static int toggle; - // Check if indexing thread done - if (indexingstatus) { - statusBar()->message(""); - indexingstatus = false; - // Make sure we reopen the db to get the results. - LOGINFO(("Indexing done: closing query database\n")); - rcldb->close(); - } else if (indexingdone == 0) { - if (toggle < 9) { - statusBar()->message(tr("Indexing in progress")); - } else { - statusBar()->message(""); - } - if (toggle >= 10) - toggle = 0; - toggle++; - } - if (recollNeedsExit) - fileExit(); } - -void RecollMain::fileStart_IndexingAction_activated() +void RclMainBase::startSimpleSearch() { - if (indexingdone == 1) - startindexing = 1; } - -// Note that all our 'urls' are like : file://... -static string urltolocalpath(string url) +void RclMainBase::resPageUpOrBack() { - return url.substr(7, string::npos); } - -// Translate paragraph number in list window to doc number. This depends on -// how we format the title etc.. -int RecollMain::reldocnumfromparnum(int par) +void RclMainBase::resPageDownOrNext() { - std::map<int,int>::iterator it = pageParaToReldocnums.find(par); - int rdn; - if (it != pageParaToReldocnums.end()) { - rdn = it->second; - } else { - rdn = -1; - } - LOGDEB1(("reldocnumfromparnum: par %d reldoc %d\n", par, rdn)); - return rdn; } - -// Double click in result list: use external viewer to display file -void RecollMain::reslistTE_doubleClicked(int par, int) +void RclMainBase::resultPageBack() { - LOGDEB(("RecollMain::reslistTE_doubleClicked: par %d\n", par)); - reslist_dblclck = true; - int reldocnum = reldocnumfromparnum(par); - if (reldocnum < 0) - return; - startNativeViewer(reslist_winfirst + reldocnum); } - - -// Display preview for the selected document, and highlight entry. The -// paragraph number is doc number in window + 1 -// We don't actually do anything but start a timer because we want to -// check first if this might be a double click -void RecollMain::reslistTE_clicked(int par, int car) +void RclMainBase::showResultPage() { - if (reslist_waitingdbl) - return; - LOGDEB(("RecollMain::reslistTE_clicked:wfirst %d par %d char %d drg %d\n", - reslist_winfirst, par, car, reslist_mouseDrag)); - if (reslist_winfirst == -1 || reslist_mouseDrag) - return; - - // remember par and car - reslist_par = par; - reslist_car = car; - reslist_waitingdbl = true; - reslist_dblclck = false; - // Wait to see if there's going to be a dblclck - QTimer::singleShot(150, this, SLOT(reslistTE_delayedclick()) ); } - -// This gets called by a timer 100mS after a single click in the -// result list. We don't want to start a preview if the user has -// requested a native viewer by double-clicking -void RecollMain::reslistTE_delayedclick() +void RclMainBase::previewClosed( QWidget * ) { - LOGDEB(("RecollMain::reslistTE_delayedclick:\n")); - reslist_waitingdbl = false; - if (reslist_dblclck) { - LOGDEB1(("RecollMain::reslistTE_delayedclick: dbleclick\n")); - reslist_dblclck = false; - return; - } - - int par = reslist_par; - - // Erase everything back to white - { - QColor color("white"); - for (int i = 1; i < reslistTE->paragraphs(); i++) - reslistTE->setParagraphBackgroundColor(i, color); - } - - // Color the new active paragraph - QColor color("lightblue"); - reslistTE->setParagraphBackgroundColor(par, color); - - // Document number - int reldocnum = reldocnumfromparnum(par); - - if (reldocnum < 0) { - // Bad number: must have clicked on header. Show details of query - QString desc = tr("Query details") + ": " + - QString::fromUtf8(currentQueryData.description.c_str()); - QMessageBox::information(this, tr("Query details"), desc); - return; - } else { - startPreview(reslist_winfirst + reldocnum); - } } - -// User asked to start query. Send it to the db aand call -// listNextPB_clicked to fetch and display the first page of results -void RecollMain::startSimpleSearch() +void RclMainBase::showAdvSearchDialog() { - LOGDEB(("RecollMain::queryText_returnPressed()\n")); - // The db may have been closed at the end of indexing - Rcl::AdvSearchData sdata; - - QCString u8 = queryText->text().utf8(); - if (allTermsCB->isChecked()) - sdata.allwords = u8; - else - sdata.orwords = u8; - - startAdvSearch(sdata); } - -// Execute an advanced search query. The parameters normally come from -// the advanced search dialog -void RecollMain::startAdvSearch(Rcl::AdvSearchData sdata) +void RclMainBase::showSortDialog() { - LOGDEB(("RecollMain::startAdvSearch\n")); - // The db may have been closed at the end of indexing - string reason; - if (!maybeOpenDb(reason)) { - QMessageBox::critical(0, "Recoll", QString(reason.c_str())); - exit(1); - } - - reslist_winfirst = -1; - - if (!rcldb->setQuery(sdata, prefs_queryStemLang.length() > 0 ? - Rcl::Db::QO_STEM : Rcl::Db::QO_NONE, - prefs_queryStemLang.ascii())) - return; - curPreview = 0; - if (docsource) - delete docsource; - - if (sortwidth > 0) { - DocSequenceDb myseq(rcldb, tr("Query results")); - docsource = new DocSeqSorted(myseq, sortwidth, sortspecs, - tr("Query results (sorted)")); - } else { - docsource = new DocSequenceDb(rcldb, tr("Query results")); - } - currentQueryData = sdata; - showResultPage(); } - -// Page Up/Down: we don't try to check if current paragraph is last or -// first. We just page up/down and check if viewport moved. If it did, -// fair enough, else we go to next/previous result page. -void RecollMain::resPageUpOrBack() +void RclMainBase::showAboutDialog() { - int vpos = reslistTE->contentsY(); - reslistTE->moveCursor(QTextEdit::MovePgUp, false); - if (vpos == reslistTE->contentsY()) - resultPageBack(); } -void RecollMain::resPageDownOrNext() +void RclMainBase::startManual() { - int vpos = reslistTE->contentsY(); - reslistTE->moveCursor(QTextEdit::MovePgDown, false); - LOGDEB(("RecollMain::resPageDownOrNext: vpos before %d, after %d\n", - vpos, reslistTE->contentsY())); - if (vpos == reslistTE->contentsY()) - showResultPage(); } - -// Show previous page of results. We just set the current number back -// 2 pages and show next page. -void RecollMain::resultPageBack() +void RclMainBase::showDocHistory() { - if (reslist_winfirst <= 0) - return; - reslist_winfirst -= 2 * prefs_respagesize; - showResultPage(); } - - -// Fill up result list window with next screen of hits -void RecollMain::showResultPage() +void RclMainBase::searchTextChanged( const QString & ) { - if (!docsource) - return; - - int percent; - Rcl::Doc doc; - - int resCnt = docsource->getResCnt(); - - LOGDEB(("showResultPage: rescnt %d, winfirst %d\n", resCnt, - reslist_winfirst)); - - pageParaToReldocnums.clear(); - - // If we are already on the last page, nothing to do: - if (reslist_winfirst >= 0 && - (reslist_winfirst + prefs_respagesize > resCnt)) { - nextPageAction->setEnabled(false); - return; - } - - if (reslist_winfirst < 0) { - reslist_winfirst = 0; - prevPageAction->setEnabled(false); - } else { - prevPageAction->setEnabled(true); - reslist_winfirst += prefs_respagesize; - } - - bool gotone = false; - reslistTE->clear(); - - int last = MIN(resCnt-reslist_winfirst, prefs_respagesize); - - - // Insert results if any in result list window. We have to send - // the text to the widgets, because we need the paragraph number - // each time we add a result paragraph (its diffult and - // error-prone to compute the paragraph numbers in parallel. We - // would like to disable updates while we're doing this, but - // couldn't find a way to make it work, the widget seems to become - // confused if appended while updates are disabled - // reslistTE->setUpdatesEnabled(false); - for (int i = 0; i < last; i++) { - string sh; - doc.erase(); - - if (!docsource->getDoc(reslist_winfirst + i, doc, &percent, &sh)) { - // This may very well happen for history if the doc has - // been removed since. So don't treat it as fatal. - doc.abstract = string(tr("Unavailable document").utf8()); - } - if (i == 0) { - // Display header - // We could use a <title> but the textedit doesnt display - // it prominently - reslistTE->append("<qt><head></head><body>"); - QString line = "<p><font size=+1><b>"; - line += docsource->title().c_str(); - line += "</b></font><br>"; - reslistTE->append(line); - line = tr("<b>Displaying results starting at index" - " %1 (maximum set size %2)</b></p>\n") - .arg(reslist_winfirst+1) - .arg(resCnt); - reslistTE->append(line); - } - - gotone = true; - - // Result list entry display: this must be exactly one paragraph - // TOBEDONE - // - move abstract/keywords to Detail window ? - // - keywords matched ? - // - language ? - // - size ? - - string result; - if (!sh.empty()) - result += string("<p><b>") + sh + "</p>\n<p>"; - else - result = "<p>"; - - string img_name; - if (prefs_showicons) { - string iconname = rclconfig->getMimeIconName(doc.mimetype); - if (iconname.empty()) - iconname = "document"; - string imgfile = iconsdir + "/" + iconname + ".png"; - - LOGDEB1(("Img file; %s\n", imgfile.c_str())); - QImage image(imgfile.c_str()); - if (!image.isNull()) { - img_name = string("img_") + iconname; - QMimeSourceFactory::defaultFactory()-> - setImage(img_name.c_str(), image); - } - } - - char perbuf[10]; - sprintf(perbuf, "%3d%%", percent); - if (doc.title.empty()) - doc.title = path_getsimple(doc.url); - char datebuf[100]; - datebuf[0] = 0; - if (!doc.dmtime.empty() || !doc.fmtime.empty()) { - time_t mtime = doc.dmtime.empty() ? - atol(doc.fmtime.c_str()) : atol(doc.dmtime.c_str()); - struct tm *tm = localtime(&mtime); - strftime(datebuf, 99, - "<i>Modified:</i> %Y-%m-%d %H:%M:%S", tm); - } - string abst = escapeHtml(doc.abstract); - LOGDEB1(("Abstract: {%s}\n", abst.c_str())); - if (!img_name.empty()) { - result += "<img source=\"" + img_name + "\" align=\"left\">"; - } - result += string(perbuf) + " <b>" + doc.title + "</b><br>" + - doc.mimetype + " " + - (datebuf[0] ? string(datebuf) + "<br>" : string("<br>")) + - (!abst.empty() ? abst + "<br>" : string("")) + - (!doc.keywords.empty() ? doc.keywords + "<br>" : string("")) + - "<i>" + doc.url + +"</i><br></p>\n"; - - QString str = QString::fromUtf8(result.c_str(), result.length()); - reslistTE->append(str); - - pageParaToReldocnums[reslistTE->paragraphs()-1] = i; - } - - if (gotone) { - reslistTE->append("</body></qt>"); - reslistTE->setCursorPosition(0,0); - reslistTE->ensureCursorVisible(); - } else { - // Restore first in win parameter that we shouln't have incremented - reslistTE->append(tr("<p>" - /*"<img align=\"left\" source=\"myimage\">"*/ - "<b>No results found</b>" - "<br>")); - reslist_winfirst -= prefs_respagesize; - if (reslist_winfirst < 0) - reslist_winfirst = -1; - } - - //reslistTE->setUpdatesEnabled(true);reslistTE->sync();reslistTE->repaint(); - -#if 0 - { - FILE *fp = fopen("/tmp/reslistdebug", "w"); - if (fp) { - const char *text = (const char *)reslistTE->text().utf8(); - //const char *text = alltext.c_str(); - fwrite(text, 1, strlen(text), fp); - fclose(fp); - } - } -#endif - - if (reslist_winfirst < 0 || - (reslist_winfirst >= 0 && - reslist_winfirst + prefs_respagesize >= resCnt)) { - nextPageAction->setEnabled(false); - } else { - nextPageAction->setEnabled(true); - } } - -// 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 RecollMain::previewClosed(Preview *w) +void RclMainBase::showUIPrefs() { - if (w == curPreview) { - LOGDEB(("Active preview closed\n")); - curPreview = 0; - } else { - LOGDEB(("Old preview closed\n")); - } - delete w; } - -// Open advanced search dialog. -void RecollMain::showAdvSearchDialog() +void RclMainBase::setUIPrefs() { - if (asearchform == 0) { - asearchform = new advsearch(0, tr("Advanced search"), FALSE, - WStyle_Customize | WStyle_NormalBorder | - WStyle_Title | WStyle_SysMenu); - asearchform->setSizeGripEnabled(FALSE); - connect(asearchform, SIGNAL(startSearch(Rcl::AdvSearchData)), - this, SLOT(startAdvSearch(Rcl::AdvSearchData))); - asearchform->show(); - } else { - // Close and reopen, in hope that makes us visible... - asearchform->close(); - asearchform->show(); - } -} - -void RecollMain::showSortDialog() -{ - if (sortform == 0) { - sortform = new SortForm(0, tr("Sort criteria"), FALSE, - WStyle_Customize | WStyle_NormalBorder | - WStyle_Title | WStyle_SysMenu); - sortform->setSizeGripEnabled(FALSE); - connect(sortform, SIGNAL(sortDataChanged(int, RclSortSpec)), - this, SLOT(sortDataChanged(int, RclSortSpec))); - sortform->show(); - } else { - // Close and reopen, in hope that makes us visible... - sortform->close(); - sortform->show(); - } - -} - -void RecollMain::showUIPrefs() -{ - if (uiprefs == 0) { - uiprefs = new UIPrefsDialog(0, tr("User interface preferences"), FALSE, - WStyle_Customize | WStyle_NormalBorder | - WStyle_Title | WStyle_SysMenu); - uiprefs->setSizeGripEnabled(FALSE); - connect(uiprefs, SIGNAL(uiprefsDone()), this, SLOT(setUIPrefs())); - uiprefs->show(); - } else { - // Close and reopen, in hope that makes us visible... - uiprefs->close(); - uiprefs->show(); - } -} - -/** - * Open a preview window for a given document, or load it into new tab of - * existing window. - * - * @param docnum db query index - */ -void RecollMain::startPreview(int docnum) -{ - Rcl::Doc doc; - if (!docsource->getDoc(docnum, doc, 0)) { - QMessageBox::warning(0, "Recoll", - tr("Cannot retrieve document info" - " from database")); - return; - } - - // Check file exists in file system - string fn = urltolocalpath(doc.url); - struct stat st; - if (stat(fn.c_str(), &st) < 0) { - QMessageBox::warning(0, "Recoll", tr("Cannot access document file: ") + - fn.c_str()); - return; - } - - if (curPreview == 0) { - curPreview = new Preview(0, tr("Preview")); - if (curPreview == 0) { - QMessageBox::warning(0, tr("Warning"), - tr("Can't create preview window"), - QMessageBox::Ok, - QMessageBox::NoButton); - return; - } - - curPreview->setCaption(queryText->text()); - connect(curPreview, SIGNAL(previewClosed(Preview *)), - this, SLOT(previewClosed(Preview *))); - curPreview->show(); - } else { - if (curPreview->makeDocCurrent(fn, doc)) { - // Already there - return; - } - (void)curPreview->addEditorTab(); - } - history->enterDocument(fn, doc.ipath); - curPreview->loadFileInCurrentTab(fn, st.st_size, doc); -} - -void RecollMain::startNativeViewer(int docnum) -{ - Rcl::Doc doc; - if (!docsource->getDoc(docnum, doc, 0, 0)) { - QMessageBox::warning(0, "Recoll", - tr("Cannot retrieve document info" - " from database")); - return; - } - - // Look for appropriate viewer - string cmd = rclconfig->getMimeViewerDef(doc.mimetype); - if (cmd.length() == 0) { - QMessageBox::warning(0, "Recoll", - tr("No external viewer configured for mime type ") - + doc.mimetype.c_str()); - return; - } - - string fn = urltolocalpath(doc.url); - - // Substitute %u (url) and %f (file name) inside prototype command - string ncmd; - string::const_iterator it1; - for (it1 = cmd.begin(); it1 != cmd.end();it1++) { - if (*it1 == '%') { - if (++it1 == cmd.end()) { - ncmd += '%'; - break; - } - if (*it1 == '%') - ncmd += '%'; - if (*it1 == 'u') - ncmd += "'" + doc.url + "'"; - if (*it1 == 'f') - ncmd += "'" + fn + "'"; - } else { - ncmd += *it1; - } - } - - ncmd += " &"; - QStatusBar *stb = statusBar(); - if (stb) { - QString msg = tr("Executing: [") + ncmd.c_str() + "]"; - stb->message(msg, 5000); - stb->repaint(false); - XFlush(qt_xdisplay()); - } - history->enterDocument(fn, doc.ipath); - system(ncmd.c_str()); -} - - -void RecollMain::showAboutDialog() -{ - string vstring = string("Recoll ") + rclversion + - "<br>" + "http://www.recoll.org"; - QMessageBox::information(this, tr("About Recoll"), vstring.c_str()); -} - -void RecollMain::startManual() -{ - startHelpBrowser(); -} - -void RecollMain::showDocHistory() -{ - LOGDEB(("RecollMain::showDocHistory\n")); - reslist_winfirst = -1; - curPreview = 0; - if (docsource) - delete docsource; - - if (sortwidth > 0) { - DocSequenceHistory myseq(rcldb, history, tr("Document history")); - docsource = new DocSeqSorted(myseq, sortwidth, sortspecs, - tr("Document history (sorted)")); - } else { - docsource = new DocSequenceHistory(rcldb, history, - tr("Document history")); - } - currentQueryData.erase(); - currentQueryData.description = tr("History data").utf8(); - showResultPage(); -} - - -void RecollMain::searchTextChanged(const QString & text) -{ - if (text.isEmpty()) { - searchPB->setEnabled(false); - clearqPB->setEnabled(false); - } else { - searchPB->setEnabled(true); - clearqPB->setEnabled(true); - } - -} - -void RecollMain::sortDataChanged(int cnt, RclSortSpec spec) -{ - LOGDEB(("RecollMain::sortDataChanged\n")); - sortwidth = cnt; - sortspecs = spec; -} - -// This could be handled inside the dialog's accept(), but we may want to -// do something (ie: redisplay reslist?) -void RecollMain::setUIPrefs() -{ - if (!uiprefs) - return; - LOGDEB(("Recollmain::setUIPrefs\n")); - prefs_showicons = uiprefs->useIconsCB->isChecked(); - prefs_respagesize = uiprefs->pageLenSB->value(); - - prefs_reslistfontfamily = uiprefs->reslistFontFamily; - prefs_reslistfontsize = uiprefs->reslistFontSize; - if (prefs_reslistfontfamily.length()) { - QFont nfont(prefs_reslistfontfamily, prefs_reslistfontsize); - reslistTE->setFont(nfont); - } else { - reslistTE->setFont(this->font()); - } - - if (uiprefs->stemLangCMB->currentItem() == 0) { - prefs_queryStemLang = ""; - } else { - prefs_queryStemLang = uiprefs->stemLangCMB->currentText(); - } }