diff --git a/src/internfile/internfile.cpp b/src/internfile/internfile.cpp index b435ea11..024f475c 100644 --- a/src/internfile/internfile.cpp +++ b/src/internfile/internfile.cpp @@ -648,7 +648,7 @@ FileInterner::Status FileInterner::internfile(Rcl::Doc& doc, string& ipath) return FIAgain; } -// Automatic cleanup of iDocTempFile's temp dir +// Automatic cleanup of iDocToFile's temp dir class DirWiper { public: string dir; @@ -671,14 +671,15 @@ class DirWiper { // return by the DirWiper object // - The output temporary file which is held in a reference-counted // object and will be deleted when done with. -bool FileInterner::idocTempFile(TempFile& otemp, RclConfig *cnf, - const string& fn, - const string& ipath, - const string& mtype) +bool FileInterner::idocToFile(TempFile& otemp, const string& tofile, + RclConfig *cnf, + const string& fn, + const string& ipath, + const string& mtype) { struct stat st; if (stat(fn.c_str(), &st) < 0) { - LOGERR(("FileInterner::idocTempFile: can't stat [%s]\n", fn.c_str())); + LOGERR(("FileInterner::idocToFile: can't stat [%s]\n", fn.c_str())); return false; } @@ -693,29 +694,39 @@ bool FileInterner::idocTempFile(TempFile& otemp, RclConfig *cnf, string mipath = ipath; Status ret = interner.internfile(doc, mipath); if (ret == FileInterner::FIError) { - LOGERR(("FileInterner::idocTempFile: internfile() failed\n")); + LOGERR(("FileInterner::idocToFile: internfile() failed\n")); return false; } - TempFile temp(new TempFileInternal(cnf->getSuffixFromMimeType(mtype))); - if (!temp->ok()) { - LOGERR(("FileInterner::idocTempFile: cannot create temporary file")); - return false; + + string filename; + TempFile temp; + if (tofile.empty()) { + TempFile temp1(new TempFileInternal(cnf->getSuffixFromMimeType(mtype))); + temp = temp1; + if (!temp->ok()) { + LOGERR(("FileInterner::idocToFile: cant create temporary file")); + return false; + } + filename = temp->filename(); + } else { + filename = tofile; } - int fd = open(temp->filename(), O_WRONLY); + int fd = open(filename.c_str(), O_WRONLY|O_CREAT, 0600); if (fd < 0) { - LOGERR(("FileInterner::idocTempFile: open(%s) failed errno %d\n", - temp->filename(), errno)); + LOGERR(("FileInterner::idocToFile: open(%s) failed errno %d\n", + filename.c_str(), errno)); return false; } const string& dt = doc.text; if (write(fd, dt.c_str(), dt.length()) != (int)dt.length()) { close(fd); - LOGERR(("FileInterner::idocTempFile: write to %s failed errno %d\n", - temp->filename(), errno)); + LOGERR(("FileInterner::idocToFile: write to %s failed errno %d\n", + filename.c_str(), errno)); return false; } close(fd); - otemp = temp; + if (tofile.empty()) + otemp = temp; return true; } diff --git a/src/internfile/internfile.h b/src/internfile/internfile.h index 9753dab0..857ea653 100644 --- a/src/internfile/internfile.h +++ b/src/internfile/internfile.h @@ -102,19 +102,21 @@ class FileInterner { /* In case we see an html version, it's set aside and can be recovered */ const string& get_html() {return m_html;} - /** Utility function: extract internal document into temporary file. + /** Extract internal document into temporary file. * This is used mainly for starting an external viewer for a * subdocument (ie: mail attachment). * @return true for success. * @param temp output reference-counted temp file object (goes - * away magically) + * away magically). Only used if tofile.empty() + * @param tofile output file if not null * @param cnf The recoll config * @param fn The main document from which to extract * @param ipath The internal path to the subdoc * @param mtype The target mime type (we don't want to decode to text!) */ - static bool idocTempFile(TempFile& temp, RclConfig *cnf, const string& fn, - const string& ipath, const string& mtype); + static bool idocToFile(TempFile& temp, const string& tofile, + RclConfig *cnf, const string& fn, + const string& ipath, const string& mtype); const string& getReason() const {return m_reason;} static void getMissingExternal(string& missing); diff --git a/src/qtgui/rclmain_w.cpp b/src/qtgui/rclmain_w.cpp index be31ca35..b0e1207d 100644 --- a/src/qtgui/rclmain_w.cpp +++ b/src/qtgui/rclmain_w.cpp @@ -33,12 +33,18 @@ using std::pair; #include #include + #if (QT_VERSION < 0x040000) #include #include #include #include +#include +#else +#include +#define QFileDialog Q3FileDialog #endif + #include #include #include @@ -196,6 +202,8 @@ void RclMain::init() this, SLOT(enablePrevPage(bool))); connect(resList, SIGNAL(docEditClicked(int)), this, SLOT(startNativeViewer(int))); + connect(resList, SIGNAL(docSaveToFileClicked(int)), + this, SLOT(saveDocToFile(int))); connect(resList, SIGNAL(editRequested(Rcl::Doc)), this, SLOT(startNativeViewer(Rcl::Doc))); @@ -793,6 +801,32 @@ void RclMain::ssearchAddTerm(QString term) sSearch->queryText->setEditText(text); } +void RclMain::saveDocToFile(int docnum) +{ + Rcl::Doc doc; + if (!resList->getDoc(docnum, doc)) { + QMessageBox::warning(0, "Recoll", + tr("Cannot retrieve document info" + " from database")); + return; + } + string fn = urltolocalpath(doc.url); + QString s = + QFileDialog::getSaveFileName(path_home().c_str(), + "", this, + tr("Save file dialog"), + tr("Choose a file name to save under")); + string tofile((const char *)s.toLocal8Bit()); + TempFile temp; // not used + if (!FileInterner::idocToFile(temp, tofile, rclconfig, fn, + doc.ipath, doc.mimetype)) { + QMessageBox::warning(0, "Recoll", + tr("Cannot extract document or create " + "temporary file")); + return; + } +} + void RclMain::startNativeViewer(int docnum) { Rcl::Doc doc; @@ -879,7 +913,7 @@ void RclMain::startNativeViewer(Rcl::Doc doc) // There is an ipath and the command does not know about // them. We need a temp file. TempFile temp; - if (!FileInterner::idocTempFile(temp, rclconfig, fn, + if (!FileInterner::idocToFile(temp, string(), rclconfig, fn, doc.ipath, doc.mimetype)) { QMessageBox::warning(0, "Recoll", tr("Cannot extract document or create " diff --git a/src/qtgui/rclmain_w.h b/src/qtgui/rclmain_w.h index c16f9f19..2705bade 100644 --- a/src/qtgui/rclmain_w.h +++ b/src/qtgui/rclmain_w.h @@ -97,6 +97,7 @@ public slots: virtual void startPreview(Rcl::Doc doc); virtual void startNativeViewer(int docnum); virtual void startNativeViewer(Rcl::Doc doc); + virtual void saveDocToFile(int docnum); virtual void previewNextInTab(Preview *, int sid, int docnum); virtual void previewPrevInTab(Preview *, int sid, int docnum); virtual void previewExposed(Preview *, int sid, int docnum); diff --git a/src/qtgui/reslist.cpp b/src/qtgui/reslist.cpp index 056d4c01..f0a999b2 100644 --- a/src/qtgui/reslist.cpp +++ b/src/qtgui/reslist.cpp @@ -530,6 +530,11 @@ RCLPOPUP *ResList::createPopupMenu(const QPoint& pos) popup->insertItem(tr("&Edit"), this, SLOT(menuEdit())); popup->insertItem(tr("Copy &File Name"), this, SLOT(menuCopyFN())); popup->insertItem(tr("Copy &URL"), this, SLOT(menuCopyURL())); + Rcl::Doc doc; + if (getDoc(m_popDoc, doc) && !doc.ipath.empty()) { + popup->insertItem(tr("Save to File"), this, SLOT(menuSaveToFile())); + } + popup->insertItem(tr("Find &similar documents"), this, SLOT(menuExpand())); popup->insertItem(tr("P&arent document/folder"), this, SLOT(menuSeeParent())); @@ -540,6 +545,10 @@ void ResList::menuPreview() { emit docPreviewClicked(m_popDoc, 0); } +void ResList::menuSaveToFile() +{ + emit docSaveToFileClicked(m_popDoc); +} void ResList::menuSeeParent() { diff --git a/src/qtgui/reslist.h b/src/qtgui/reslist.h index 6cf8bf1f..a5e44aa2 100644 --- a/src/qtgui/reslist.h +++ b/src/qtgui/reslist.h @@ -90,6 +90,7 @@ class ResList : public QTEXTBROWSER virtual void resultPageNext(); // Next (or first) page of results virtual void displayPage(); // Display current page virtual void menuPreview(); + virtual void menuSaveToFile(); virtual void menuEdit(); virtual void menuCopyFN(); virtual void menuCopyURL(); @@ -108,6 +109,7 @@ class ResList : public QTEXTBROWSER void prevPageAvailable(bool); void docEditClicked(int); void docPreviewClicked(int, int); + void docSaveToFileClicked(int); void previewRequested(Rcl::Doc); void editRequested(Rcl::Doc); void headerClicked();