Switch to side-by-side view

--- a
+++ b/src/qtgui/multisave.cpp
@@ -0,0 +1,140 @@
+/* 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 <stdio.h>
+
+#include <string>
+#include <set>
+#include <sstream>
+using namespace std;
+
+#include <QWidget>
+#include <QFileDialog>
+#include <QMessageBox>
+
+#include "recoll.h"
+#include "multisave.h"
+#include "smallut.h"
+#include "debuglog.h"
+#include "pathut.h"
+#include "internfile.h"
+
+const unsigned int maxlen = 200;
+
+void multiSave(QWidget *p, vector<Rcl::Doc>& docs)
+{
+    QFileDialog fdialog(p, p->tr("Create or choose save directory"));
+    fdialog.setAcceptMode(QFileDialog::AcceptSave);
+    fdialog.setFileMode(QFileDialog::Directory);
+    fdialog.setOption(QFileDialog::ShowDirsOnly);
+    if (fdialog.exec() == 0) 
+	return;
+    QStringList dirl = fdialog.selectedFiles();
+    if (dirl.size() != 1) {
+	// Can't happen ?
+	QMessageBox::warning(0, "Recoll",
+			     p->tr("Choose exactly one directory"));
+	return;
+    }
+    string dir((const char *)dirl[0].toLocal8Bit());
+    LOGDEB2(("multiSave: got dir %s\n", dir.c_str()));
+
+    /* Save doc to files in target directory. Issues:
+       - It is quite common to have docs in the array with the save
+         file names, e.g. all messages in a folder have the save file
+         name (the folder's).
+       - There is no warranty that the ipath is going to be acceptable
+         as a file name or interesting at all. We don't use it. 
+       - We have to make sure the names don't end up too long.
+
+       If collisions occur, we add a numeric infix (e.g. somefile.23.pdf).
+
+       We never overwrite existing files and don't give the user an
+       option to do it (they can just as well save to an empty
+       directory and use the file manager to accomplish whatever they
+       want).
+
+       We don't try hard to protect against race-conditions
+       though. The existing file names are read before beginning the
+       save sequence, and collisions appearing after this are handled
+       by aborting. There is a window between existence check and creation
+       because idoctofile does not use O_EXCL
+    */
+    set<string> existingNames;
+    string reason;
+    if (!readdir(dir, reason, existingNames)) {
+	QMessageBox::warning(0, "Recoll",
+			     p->tr("Could not read directory: ") +
+			     QString::fromLocal8Bit(reason.c_str()));
+	return;
+    }
+
+    set<string> toBeCreated;
+    vector<string> filenames;
+    for (vector<Rcl::Doc>::iterator it = docs.begin(); it != docs.end(); it++) {
+	string utf8fn;
+	it->getmeta(Rcl::Doc::keyfn, &utf8fn);
+	string suffix = path_suffix(utf8fn);
+	if (suffix.empty() || suffix.size() > 10) {
+	    suffix = theconfig->getSuffixFromMimeType(it->mimetype);
+	}
+	string simple = path_basename(utf8fn, suffix);
+	if (simple.empty())
+	    simple = "rclsave";
+	if (simple.size() > maxlen) {
+	    simple = simple.substr(0, maxlen);
+	}
+	for  (int vers = 0; ; vers++) {
+	    ostringstream ss;
+	    ss << simple;
+	    if (vers)
+		ss << "." << vers;
+	    if (!suffix.empty())
+		ss << suffix;
+
+	    string fn = 
+		(const char *)QString::fromUtf8(ss.str().c_str()).toLocal8Bit();
+	    if (existingNames.find(fn) == existingNames.end() &&
+		toBeCreated.find(fn) == toBeCreated.end()) {
+		toBeCreated.insert(fn);
+		filenames.push_back(fn);
+		break;
+	    }
+	}
+    }
+    
+    for (unsigned int i = 0; i != docs.size(); i++) {
+	string fn = path_cat(dir, filenames[i]);
+	if (access(fn.c_str(), 0) == 0) {
+	    QMessageBox::warning(0, "Recoll",
+				 p->tr("Unexpected file name collision, "
+				       "cancelling."));
+	    return;
+	}
+	// There is still a race condition here, should we care ?
+	TempFile temp;// not used
+	if (!FileInterner::idocToFile(temp, fn, theconfig, docs[i])) {
+	    QMessageBox::warning(0, "Recoll",
+				 p->tr("Cannot extract document: ") +
+				 QString::fromLocal8Bit(docs[i].url.c_str()) +
+				 " | " +
+				 QString::fromLocal8Bit(docs[i].ipath.c_str())
+		);
+	}
+    }
+}