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.
277 ->message(_("Autosave failed!"));
280 if (pid == 0) { // we are the child so...
290 void autoSave(BufferView * bv)
291 // should probably be moved into BufferList (Lgb)
292 // Perfect target for a thread...
297 if (bv->buffer()->isBakClean() || bv->buffer()->isReadonly()) {
298 // We don't save now, but we'll try again later
299 bv->buffer()->resetAutosaveTimers();
303 // emit message signal.
304 bv->buffer()->message(_("Autosaving current document..."));
306 // create autosave filename
307 string fname = bv->buffer()->filePath();
309 fname += onlyFilename(bv->buffer()->fileName());
312 AutoSaveBuffer autosave(*bv, FileName(fname));
315 bv->buffer()->markBakClean();
316 bv->buffer()->resetAutosaveTimers();
321 // Copyright CHT Software Service GmbH
324 // create new file with template
327 void newFile(LyXView & lv, string const & filename)
329 // Split argument by :
331 string tmpname = split(filename, name, ':');
332 LYXERR(Debug::INFO) << "Arg is " << filename
333 << "\nName is " << name
334 << "\nTemplate is " << tmpname << endl;
336 Buffer * const b = newFile(name, tmpname);
342 // Insert plain text file (if filename is empty, prompt for one)
343 void insertPlaintextFile(BufferView * bv, string const & f, bool asParagraph)
348 docstring const tmpstr =
349 getContentsOfPlaintextFile(bv, f, asParagraph);
354 Cursor & cur = bv->cursor();
355 cap::replaceSelection(cur);
358 cur.innerText()->insertStringAsParagraphs(cur, tmpstr);
360 cur.innerText()->insertStringAsLines(cur, tmpstr);
364 docstring const getContentsOfPlaintextFile(BufferView * bv, string const & f,
370 FileDialog fileDlg(_("Select file to insert"),
372 ? LFUN_FILE_INSERT_PLAINTEXT_PARA
373 : LFUN_FILE_INSERT_PLAINTEXT) );
375 FileDialog::Result result =
376 fileDlg.open(from_utf8(bv->buffer()->filePath()),
377 FileFilterList(), docstring());
379 if (result.first == FileDialog::Later)
382 fname = makeAbsPath(to_utf8(result.second));
388 if (!fs::is_readable(fname.toFilesystemEncoding())) {
389 docstring const error = from_ascii(strerror(errno));
390 docstring const file = makeDisplayPath(fname.absFilename(), 50);
391 docstring const text =
392 bformat(_("Could not read the specified document\n"
393 "%1$s\ndue to the error: %2$s"), file, error);
394 Alert::error(_("Could not read file"), text);
398 ifstream ifs(fname.toFilesystemEncoding().c_str());
400 docstring const error = from_ascii(strerror(errno));
401 docstring const file = makeDisplayPath(fname.absFilename(), 50);
402 docstring const text =
403 bformat(_("Could not open the specified document\n"
404 "%1$s\ndue to the error: %2$s"), file, error);
405 Alert::error(_("Could not open file"), text);
409 ifs.unsetf(ios::skipws);
410 istream_iterator<char> ii(ifs);
411 istream_iterator<char> end;
412 #if !defined(USE_INCLUDED_STRING) && !defined(STD_STRING_IS_GOOD)
413 // We use this until the compilers get better...
414 std::vector<char> tmp;
415 copy(ii, end, back_inserter(tmp));
416 string const tmpstr(tmp.begin(), tmp.end());
418 // This is what we want to use and what we will use once the
419 // compilers get good enough.
420 //string tmpstr(ii, end); // yet a reason for using std::string
421 // alternate approach to get the file into a string:
423 copy(ii, end, back_inserter(tmpstr));
426 // FIXME UNICODE: We don't know the encoding of the file
427 docstring file_content = from_utf8(tmpstr);
428 if (file_content.empty()) {
429 Alert::error(_("Reading not UTF-8 encoded file"),
430 _("The file is not UTF-8 encoded.\n"
431 "It will be read as local 8Bit-encoded.\n"
432 "If this does not give the correct result\n"
433 "then please change the encoding of the file\n"
434 "to UTF-8 with a program other than LyX.\n"));
435 file_content = from_local8bit(tmpstr);
438 return normalize_c(file_content);
442 // This function runs "configure" and then rereads lyx.defaults to
443 // reconfigure the automatic settings.
444 void reconfigure(LyXView & lv)
446 // emit message signal.
447 lv.message(_("Running configure..."));
449 // Run configure in user lyx directory
450 support::Path p(package().user_support());
451 string const configure_command = package().configure_command();
453 one.startscript(Systemcall::Wait, configure_command);
455 // emit message signal.
456 lv.message(_("Reloading configuration..."));
457 lyxrc.read(libFileSearch(string(), "lyxrc.defaults"));
458 // Re-read packages.lst
459 LaTeXFeatures::getAvailable();
461 Alert::information(_("System reconfigured"),
462 _("The system has been reconfigured.\n"
463 "You need to restart LyX to make use of any\n"
464 "updated document class specifications."));