3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Lars Gullik Bjønnes
7 * \author Angus Leeming
10 * \author Jürgen Vigna
12 * Full author contact details are available in file CREDITS.
20 #include "BufferList.h"
21 #include "BufferView.h"
22 #include "buffer_funcs.h"
24 #include "CutAndPaste.h"
28 #include "LaTeXFeatures.h"
35 #include "frontends/alert.h"
36 #include "frontends/Application.h"
37 #include "frontends/FileDialog.h"
38 #include "frontends/LyXView.h"
40 #include "support/FileFilterList.h"
41 #include "support/filetools.h"
42 #include "support/Forkedcall.h"
43 #include "support/fs_extras.h"
44 #include "support/lyxlib.h"
45 #include "support/Package.h"
46 #include "support/Path.h"
47 #include "support/Systemcall.h"
49 #if !defined (HAVE_FORK)
53 #include <boost/shared_ptr.hpp>
54 #include <boost/filesystem/operations.hpp>
59 using std::back_inserter;
66 using std::istream_iterator;
68 using boost::shared_ptr;
69 namespace fs = boost::filesystem;
73 using support::bformat;
74 using support::FileFilterList;
75 using support::FileName;
76 using support::ForkedProcess;
77 using support::isLyXFilename;
78 using support::libFileSearch;
79 using support::makeAbsPath;
80 using support::makeDisplayPath;
81 using support::onlyFilename;
82 using support::onlyPath;
83 using support::package;
84 using support::removeAutosaveFile;
85 using support::rename;
87 using support::Systemcall;
88 using support::tempName;
89 using support::unlink;
90 using frontend::LyXView;
92 namespace Alert = frontend::Alert;
94 // this should be static, but I need it in Buffer.cpp
95 bool quitting; // flag, that we are quitting the program
101 bool menuWrite(Buffer * buffer)
103 if (buffer->save()) {
104 LyX::ref().session().lastFiles().add(FileName(buffer->fileName()));
108 // FIXME: we don't tell the user *WHY* the save failed !!
110 docstring const file = makeDisplayPath(buffer->fileName(), 30);
112 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
113 "Do you want to rename the document and "
114 "try again?"), file);
115 int const ret = Alert::prompt(_("Rename and save?"),
116 text, 0, 1, _("&Rename"), _("&Cancel"));
119 return writeAs(buffer);
125 /** Write a buffer to a new file name and rename the buffer
126 according to the new file name.
128 This function is e.g. used by menu callbacks and
129 LFUN_BUFFER_WRITE_AS.
131 If 'newname' is empty (the default), the user is asked via a
132 dialog for the buffer's new name and location.
134 If 'newname' is non-empty and has an absolute path, that is used.
135 Otherwise the base directory of the buffer is used as the base
136 for any relative path in 'newname'.
139 bool writeAs(Buffer * buffer, string const & newname)
141 string fname = buffer->fileName();
142 string const oldname = fname;
144 if (newname.empty()) { /// No argument? Ask user through dialog
147 FileDialog fileDlg(_("Choose a filename to save document as"),
148 LFUN_BUFFER_WRITE_AS,
149 make_pair(_("Documents|#o#O"),
150 from_utf8(lyxrc.document_path)),
151 make_pair(_("Templates|#T#t"),
152 from_utf8(lyxrc.template_path)));
154 if (!isLyXFilename(fname))
157 FileFilterList const filter (_("LyX Documents (*.lyx)"));
159 FileDialog::Result result =
160 fileDlg.save(from_utf8(onlyPath(fname)),
162 from_utf8(onlyFilename(fname)));
164 if (result.first == FileDialog::Later)
167 fname = to_utf8(result.second);
172 // Make sure the absolute filename ends with appropriate suffix
173 fname = makeAbsPath(fname).absFilename();
174 if (!isLyXFilename(fname))
178 fname = makeAbsPath(newname, onlyPath(oldname)).absFilename();
180 if (fs::exists(FileName(fname).toFilesystemEncoding())) {
181 docstring const file = makeDisplayPath(fname, 30);
182 docstring text = bformat(_("The document %1$s already "
183 "exists.\n\nDo you want to "
184 "overwrite that document?"),
186 int const ret = Alert::prompt(_("Overwrite document?"),
187 text, 0, 1, _("&Overwrite"), _("&Cancel"));
193 // Ok, change the name of the buffer
194 buffer->setFileName(fname);
196 bool unnamed = buffer->isUnnamed();
197 buffer->setUnnamed(false);
198 buffer->saveCheckSum(fname);
200 if (!menuWrite(buffer)) {
201 buffer->setFileName(oldname);
202 buffer->setUnnamed(unnamed);
203 buffer->saveCheckSum(oldname);
207 removeAutosaveFile(oldname);
214 class AutoSaveBuffer : public ForkedProcess {
217 AutoSaveBuffer(Buffer & buffer, FileName const & fname)
218 : buffer_(buffer), fname_(fname) {}
220 virtual shared_ptr<ForkedProcess> clone() const
222 return shared_ptr<ForkedProcess>(new AutoSaveBuffer(*this));
228 virtual int generateChild();
235 int AutoSaveBuffer::start()
237 command_ = to_utf8(bformat(_("Auto-saving %1$s"),
238 from_utf8(fname_.absFilename())));
239 return run(DontWait);
243 int AutoSaveBuffer::generateChild()
245 // tmp_ret will be located (usually) in /tmp
246 // will that be a problem?
247 pid_t const pid = fork(); // If you want to debug the autosave
248 // you should set pid to -1, and comment out the
250 if (pid == 0 || pid == -1) {
251 // pid = -1 signifies that lyx was unable
252 // to fork. But we will do the save
256 FileName const tmp_ret(tempName(FileName(), "lyxauto"));
257 if (!tmp_ret.empty()) {
258 buffer_.writeFile(tmp_ret);
259 // assume successful write of tmp_ret
260 if (!rename(tmp_ret, fname_)) {
262 // most likely couldn't move between
263 // filesystems unless write of tmp_ret
264 // failed so remove tmp file (if it
273 // failed to write/rename tmp_ret so try writing direct
274 if (!buffer_.writeFile(fname_)) {
275 // It is dangerous to do this in the child,
276 // but safe in the parent, so...
277 if (pid == -1) // emit message signal.
278 buffer_.message(_("Autosave failed!"));
281 if (pid == 0) { // we are the child so...
291 void autoSave(BufferView * bv)
292 // should probably be moved into BufferList (Lgb)
293 // Perfect target for a thread...
295 Buffer & buf = bv->buffer();
296 if (buf.isBakClean() || buf.isReadonly()) {
297 // We don't save now, but we'll try again later
298 buf.resetAutosaveTimers();
302 // emit message signal.
303 buf.message(_("Autosaving current document..."));
305 // create autosave filename
306 string fname = buf.filePath();
308 fname += onlyFilename(buf.fileName());
311 AutoSaveBuffer autosave(buf, FileName(fname));
315 buf.resetAutosaveTimers();
320 // Copyright CHT Software Service GmbH
323 // create new file with template
326 void newFile(LyXView & lv, string const & filename)
328 // Split argument by :
330 string tmpname = split(filename, name, ':');
331 LYXERR(Debug::INFO) << "Arg is " << filename
332 << "\nName is " << name
333 << "\nTemplate is " << tmpname << endl;
335 Buffer * const b = newFile(name, tmpname);
341 // Insert plain text file (if filename is empty, prompt for one)
342 void insertPlaintextFile(BufferView * bv, string const & f, bool asParagraph)
344 docstring const tmpstr =
345 getContentsOfPlaintextFile(bv, f, asParagraph);
350 Cursor & cur = bv->cursor();
351 cap::replaceSelection(cur);
354 cur.innerText()->insertStringAsParagraphs(cur, tmpstr);
356 cur.innerText()->insertStringAsLines(cur, tmpstr);
360 docstring const getContentsOfPlaintextFile(BufferView * bv, string const & f,
366 FileDialog fileDlg(_("Select file to insert"),
368 ? LFUN_FILE_INSERT_PLAINTEXT_PARA
369 : LFUN_FILE_INSERT_PLAINTEXT) );
371 FileDialog::Result result =
372 fileDlg.open(from_utf8(bv->buffer().filePath()),
373 FileFilterList(), docstring());
375 if (result.first == FileDialog::Later)
378 fname = makeAbsPath(to_utf8(result.second));
384 if (!fs::is_readable(fname.toFilesystemEncoding())) {
385 docstring const error = from_ascii(strerror(errno));
386 docstring const file = makeDisplayPath(fname.absFilename(), 50);
387 docstring const text =
388 bformat(_("Could not read the specified document\n"
389 "%1$s\ndue to the error: %2$s"), file, error);
390 Alert::error(_("Could not read file"), text);
394 ifstream ifs(fname.toFilesystemEncoding().c_str());
396 docstring const error = from_ascii(strerror(errno));
397 docstring const file = makeDisplayPath(fname.absFilename(), 50);
398 docstring const text =
399 bformat(_("Could not open the specified document\n"
400 "%1$s\ndue to the error: %2$s"), file, error);
401 Alert::error(_("Could not open file"), text);
405 ifs.unsetf(ios::skipws);
406 istream_iterator<char> ii(ifs);
407 istream_iterator<char> end;
408 #if !defined(USE_INCLUDED_STRING) && !defined(STD_STRING_IS_GOOD)
409 // We use this until the compilers get better...
410 std::vector<char> tmp;
411 copy(ii, end, back_inserter(tmp));
412 string const tmpstr(tmp.begin(), tmp.end());
414 // This is what we want to use and what we will use once the
415 // compilers get good enough.
416 //string tmpstr(ii, end); // yet a reason for using std::string
417 // alternate approach to get the file into a string:
419 copy(ii, end, back_inserter(tmpstr));
422 // FIXME UNICODE: We don't know the encoding of the file
423 docstring file_content = from_utf8(tmpstr);
424 if (file_content.empty()) {
425 Alert::error(_("Reading not UTF-8 encoded file"),
426 _("The file is not UTF-8 encoded.\n"
427 "It will be read as local 8Bit-encoded.\n"
428 "If this does not give the correct result\n"
429 "then please change the encoding of the file\n"
430 "to UTF-8 with a program other than LyX.\n"));
431 file_content = from_local8bit(tmpstr);
434 return normalize_c(file_content);
438 // This function runs "configure" and then rereads lyx.defaults to
439 // reconfigure the automatic settings.
440 void reconfigure(LyXView & lv, string const & option)
442 // emit message signal.
443 lv.message(_("Running configure..."));
445 // Run configure in user lyx directory
446 support::Path p(package().user_support());
447 string configure_command = package().configure_command();
448 configure_command += option;
450 one.startscript(Systemcall::Wait, configure_command);
452 // emit message signal.
453 lv.message(_("Reloading configuration..."));
454 lyxrc.read(libFileSearch(string(), "lyxrc.defaults"));
455 // Re-read packages.lst
456 LaTeXFeatures::getAvailable();
458 Alert::information(_("System reconfigured"),
459 _("The system has been reconfigured.\n"
460 "You need to restart LyX to make use of any\n"
461 "updated document class specifications."));