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);
199 if (!menuWrite(buffer)) {
200 buffer->setFileName(oldname);
201 buffer->setUnnamed(unnamed);
205 removeAutosaveFile(oldname);
212 class AutoSaveBuffer : public ForkedProcess {
215 AutoSaveBuffer(BufferView & bv, FileName const & fname)
216 : bv_(bv), fname_(fname) {}
218 virtual shared_ptr<ForkedProcess> clone() const
220 return shared_ptr<ForkedProcess>(new AutoSaveBuffer(*this));
226 virtual int generateChild();
233 int AutoSaveBuffer::start()
235 command_ = to_utf8(bformat(_("Auto-saving %1$s"),
236 from_utf8(fname_.absFilename())));
237 return run(DontWait);
241 int AutoSaveBuffer::generateChild()
243 // tmp_ret will be located (usually) in /tmp
244 // will that be a problem?
245 pid_t const pid = fork(); // If you want to debug the autosave
246 // you should set pid to -1, and comment out the
248 if (pid == 0 || pid == -1) {
249 // pid = -1 signifies that lyx was unable
250 // to fork. But we will do the save
254 FileName const tmp_ret(tempName(FileName(), "lyxauto"));
255 if (!tmp_ret.empty()) {
256 bv_.buffer().writeFile(tmp_ret);
257 // assume successful write of tmp_ret
258 if (!rename(tmp_ret, fname_)) {
260 // most likely couldn't move between
261 // filesystems unless write of tmp_ret
262 // failed so remove tmp file (if it
271 // failed to write/rename tmp_ret so try writing direct
272 if (!bv_.buffer().writeFile(fname_)) {
273 // It is dangerous to do this in the child,
274 // but safe in the parent, so...
275 if (pid == -1) // emit message signal.
276 bv_.buffer().message(_("Autosave failed!"));
279 if (pid == 0) { // we are the child so...
289 void autoSave(BufferView * bv)
290 // should probably be moved into BufferList (Lgb)
291 // Perfect target for a thread...
293 if (bv->buffer().isBakClean() || bv->buffer().isReadonly()) {
294 // We don't save now, but we'll try again later
295 bv->buffer().resetAutosaveTimers();
299 // emit message signal.
300 bv->buffer().message(_("Autosaving current document..."));
302 // create autosave filename
303 string fname = bv->buffer().filePath();
305 fname += onlyFilename(bv->buffer().fileName());
308 AutoSaveBuffer autosave(*bv, FileName(fname));
311 bv->buffer().markBakClean();
312 bv->buffer().resetAutosaveTimers();
317 // Copyright CHT Software Service GmbH
320 // create new file with template
323 void newFile(LyXView & lv, string const & filename)
325 // Split argument by :
327 string tmpname = split(filename, name, ':');
328 LYXERR(Debug::INFO) << "Arg is " << filename
329 << "\nName is " << name
330 << "\nTemplate is " << tmpname << endl;
332 Buffer * const b = newFile(name, tmpname);
338 // Insert plain text file (if filename is empty, prompt for one)
339 void insertPlaintextFile(BufferView * bv, string const & f, bool asParagraph)
341 docstring const tmpstr =
342 getContentsOfPlaintextFile(bv, f, asParagraph);
347 Cursor & cur = bv->cursor();
348 cap::replaceSelection(cur);
351 cur.innerText()->insertStringAsParagraphs(cur, tmpstr);
353 cur.innerText()->insertStringAsLines(cur, tmpstr);
357 docstring const getContentsOfPlaintextFile(BufferView * bv, string const & f,
363 FileDialog fileDlg(_("Select file to insert"),
365 ? LFUN_FILE_INSERT_PLAINTEXT_PARA
366 : LFUN_FILE_INSERT_PLAINTEXT) );
368 FileDialog::Result result =
369 fileDlg.open(from_utf8(bv->buffer().filePath()),
370 FileFilterList(), docstring());
372 if (result.first == FileDialog::Later)
375 fname = makeAbsPath(to_utf8(result.second));
381 if (!fs::is_readable(fname.toFilesystemEncoding())) {
382 docstring const error = from_ascii(strerror(errno));
383 docstring const file = makeDisplayPath(fname.absFilename(), 50);
384 docstring const text =
385 bformat(_("Could not read the specified document\n"
386 "%1$s\ndue to the error: %2$s"), file, error);
387 Alert::error(_("Could not read file"), text);
391 ifstream ifs(fname.toFilesystemEncoding().c_str());
393 docstring const error = from_ascii(strerror(errno));
394 docstring const file = makeDisplayPath(fname.absFilename(), 50);
395 docstring const text =
396 bformat(_("Could not open the specified document\n"
397 "%1$s\ndue to the error: %2$s"), file, error);
398 Alert::error(_("Could not open file"), text);
402 ifs.unsetf(ios::skipws);
403 istream_iterator<char> ii(ifs);
404 istream_iterator<char> end;
405 #if !defined(USE_INCLUDED_STRING) && !defined(STD_STRING_IS_GOOD)
406 // We use this until the compilers get better...
407 std::vector<char> tmp;
408 copy(ii, end, back_inserter(tmp));
409 string const tmpstr(tmp.begin(), tmp.end());
411 // This is what we want to use and what we will use once the
412 // compilers get good enough.
413 //string tmpstr(ii, end); // yet a reason for using std::string
414 // alternate approach to get the file into a string:
416 copy(ii, end, back_inserter(tmpstr));
419 // FIXME UNICODE: We don't know the encoding of the file
420 docstring file_content = from_utf8(tmpstr);
421 if (file_content.empty()) {
422 Alert::error(_("Reading not UTF-8 encoded file"),
423 _("The file is not UTF-8 encoded.\n"
424 "It will be read as local 8Bit-encoded.\n"
425 "If this does not give the correct result\n"
426 "then please change the encoding of the file\n"
427 "to UTF-8 with a program other than LyX.\n"));
428 file_content = from_local8bit(tmpstr);
431 return normalize_c(file_content);
435 // This function runs "configure" and then rereads lyx.defaults to
436 // reconfigure the automatic settings.
437 void reconfigure(LyXView & lv)
439 // emit message signal.
440 lv.message(_("Running configure..."));
442 // Run configure in user lyx directory
443 support::Path p(package().user_support());
444 string const configure_command = package().configure_command();
446 one.startscript(Systemcall::Wait, configure_command);
448 // emit message signal.
449 lv.message(_("Reloading configuration..."));
450 lyxrc.read(libFileSearch(string(), "lyxrc.defaults"));
451 // Re-read packages.lst
452 LaTeXFeatures::getAvailable();
454 Alert::information(_("System reconfigured"),
455 _("The system has been reconfigured.\n"
456 "You need to restart LyX to make use of any\n"
457 "updated document class specifications."));