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(BufferView & bv, FileName const & fname)
218 : bv_(bv), 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 bv_.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 (!bv_.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 bv_.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 if (bv->buffer().isBakClean() || bv->buffer().isReadonly()) {
296 // We don't save now, but we'll try again later
297 bv->buffer().resetAutosaveTimers();
301 // emit message signal.
302 bv->buffer().message(_("Autosaving current document..."));
304 // create autosave filename
305 string fname = bv->buffer().filePath();
307 fname += onlyFilename(bv->buffer().fileName());
310 AutoSaveBuffer autosave(*bv, FileName(fname));
313 bv->buffer().markBakClean();
314 bv->buffer().resetAutosaveTimers();
319 // Copyright CHT Software Service GmbH
322 // create new file with template
325 void newFile(LyXView & lv, string const & filename)
327 // Split argument by :
329 string tmpname = split(filename, name, ':');
330 LYXERR(Debug::INFO) << "Arg is " << filename
331 << "\nName is " << name
332 << "\nTemplate is " << tmpname << endl;
334 Buffer * const b = newFile(name, tmpname);
340 // Insert plain text file (if filename is empty, prompt for one)
341 void insertPlaintextFile(BufferView * bv, string const & f, bool asParagraph)
343 docstring const tmpstr =
344 getContentsOfPlaintextFile(bv, f, asParagraph);
349 Cursor & cur = bv->cursor();
350 cap::replaceSelection(cur);
353 cur.innerText()->insertStringAsParagraphs(cur, tmpstr);
355 cur.innerText()->insertStringAsLines(cur, tmpstr);
359 docstring const getContentsOfPlaintextFile(BufferView * bv, string const & f,
365 FileDialog fileDlg(_("Select file to insert"),
367 ? LFUN_FILE_INSERT_PLAINTEXT_PARA
368 : LFUN_FILE_INSERT_PLAINTEXT) );
370 FileDialog::Result result =
371 fileDlg.open(from_utf8(bv->buffer().filePath()),
372 FileFilterList(), docstring());
374 if (result.first == FileDialog::Later)
377 fname = makeAbsPath(to_utf8(result.second));
383 if (!fs::is_readable(fname.toFilesystemEncoding())) {
384 docstring const error = from_ascii(strerror(errno));
385 docstring const file = makeDisplayPath(fname.absFilename(), 50);
386 docstring const text =
387 bformat(_("Could not read the specified document\n"
388 "%1$s\ndue to the error: %2$s"), file, error);
389 Alert::error(_("Could not read file"), text);
393 ifstream ifs(fname.toFilesystemEncoding().c_str());
395 docstring const error = from_ascii(strerror(errno));
396 docstring const file = makeDisplayPath(fname.absFilename(), 50);
397 docstring const text =
398 bformat(_("Could not open the specified document\n"
399 "%1$s\ndue to the error: %2$s"), file, error);
400 Alert::error(_("Could not open file"), text);
404 ifs.unsetf(ios::skipws);
405 istream_iterator<char> ii(ifs);
406 istream_iterator<char> end;
407 #if !defined(USE_INCLUDED_STRING) && !defined(STD_STRING_IS_GOOD)
408 // We use this until the compilers get better...
409 std::vector<char> tmp;
410 copy(ii, end, back_inserter(tmp));
411 string const tmpstr(tmp.begin(), tmp.end());
413 // This is what we want to use and what we will use once the
414 // compilers get good enough.
415 //string tmpstr(ii, end); // yet a reason for using std::string
416 // alternate approach to get the file into a string:
418 copy(ii, end, back_inserter(tmpstr));
421 // FIXME UNICODE: We don't know the encoding of the file
422 docstring file_content = from_utf8(tmpstr);
423 if (file_content.empty()) {
424 Alert::error(_("Reading not UTF-8 encoded file"),
425 _("The file is not UTF-8 encoded.\n"
426 "It will be read as local 8Bit-encoded.\n"
427 "If this does not give the correct result\n"
428 "then please change the encoding of the file\n"
429 "to UTF-8 with a program other than LyX.\n"));
430 file_content = from_local8bit(tmpstr);
433 return normalize_c(file_content);
437 // This function runs "configure" and then rereads lyx.defaults to
438 // reconfigure the automatic settings.
439 void reconfigure(LyXView & lv)
441 // emit message signal.
442 lv.message(_("Running configure..."));
444 // Run configure in user lyx directory
445 support::Path p(package().user_support());
446 string const configure_command = package().configure_command();
448 one.startscript(Systemcall::Wait, configure_command);
450 // emit message signal.
451 lv.message(_("Reloading configuration..."));
452 lyxrc.read(libFileSearch(string(), "lyxrc.defaults"));
453 // Re-read packages.lst
454 LaTeXFeatures::getAvailable();
456 Alert::information(_("System reconfigured"),
457 _("The system has been reconfigured.\n"
458 "You need to restart LyX to make use of any\n"
459 "updated document class specifications."));