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 "BufferView.h"
21 #include "buffer_funcs.h"
26 #include "LaTeXFeatures.h"
28 #include "lyxlayout.h"
31 #include "paragraph.h"
33 #include "frontends/Alert.h"
34 #include "frontends/Application.h"
35 #include "frontends/FileDialog.h"
36 #include "frontends/lyx_gui.h"
38 #include "support/filefilterlist.h"
39 #include "support/filetools.h"
40 #include "support/forkedcall.h"
41 #include "support/fs_extras.h"
42 #include "support/lyxlib.h"
43 #include "support/package.h"
44 #include "support/path.h"
45 #include "support/systemcall.h"
47 #if !defined (HAVE_FORK)
51 #include <boost/shared_ptr.hpp>
52 #include <boost/filesystem/operations.hpp>
58 using lyx::support::addName;
59 using lyx::support::bformat;
60 using lyx::support::destroyDir;
61 using lyx::support::FileFilterList;
62 using lyx::support::ForkedProcess;
63 using lyx::support::isLyXFilename;
64 using lyx::support::libFileSearch;
65 using lyx::support::makeAbsPath;
66 using lyx::support::makeDisplayPath;
67 using lyx::support::onlyFilename;
68 using lyx::support::onlyPath;
69 using lyx::support::package;
70 using lyx::support::removeAutosaveFile;
71 using lyx::support::rename;
72 using lyx::support::split;
73 using lyx::support::Systemcall;
74 using lyx::support::tempName;
75 using lyx::support::unlink;
77 using boost::shared_ptr;
79 namespace fs = boost::filesystem;
81 using std::back_inserter;
88 using std::istream_iterator;
91 // this should be static, but I need it in buffer.C
92 bool quitting; // flag, that we are quitting the program
99 bool menuWrite(Buffer * buffer)
101 if (buffer->save()) {
102 LyX::ref().session().addLastFile(buffer->fileName());
106 // FIXME: we don't tell the user *WHY* the save failed !!
108 docstring const file = makeDisplayPath(buffer->fileName(), 30);
110 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
111 "Do you want to rename the document and try again?"), file);
112 int const ret = Alert::prompt(_("Rename and save?"),
113 text, 0, 1, _("&Rename"), _("&Cancel"));
116 return writeAs(buffer);
122 bool writeAs(Buffer * buffer, string const & filename)
124 string fname = buffer->fileName();
125 string const oldname = fname;
127 if (filename.empty()) {
129 FileDialog fileDlg(lyx::to_utf8(_("Choose a filename to save document as")),
130 LFUN_BUFFER_WRITE_AS,
131 make_pair(string(lyx::to_utf8(_("Documents|#o#O"))),
132 string(lyxrc.document_path)),
133 make_pair(string(lyx::to_utf8(_("Templates|#T#t"))),
134 string(lyxrc.template_path)));
136 if (!isLyXFilename(fname))
139 FileFilterList const filter (lyx::to_utf8(_("LyX Documents (*.lyx)")));
141 FileDialog::Result result =
142 fileDlg.save(onlyPath(fname),
144 onlyFilename(fname));
146 if (result.first == FileDialog::Later)
149 fname = result.second;
154 // Make sure the absolute filename ends with appropriate suffix
155 fname = makeAbsPath(fname);
156 if (!isLyXFilename(fname))
161 if (fs::exists(fname)) {
162 docstring const file = makeDisplayPath(fname, 30);
163 docstring text = bformat(_("The document %1$s already exists.\n\n"
164 "Do you want to over-write that document?"), file);
165 int const ret = Alert::prompt(_("Over-write document?"),
166 text, 0, 1, _("&Over-write"), _("&Cancel"));
172 // Ok, change the name of the buffer
173 buffer->setFileName(fname);
175 bool unnamed = buffer->isUnnamed();
176 buffer->setUnnamed(false);
178 if (!menuWrite(buffer)) {
179 buffer->setFileName(oldname);
180 buffer->setUnnamed(unnamed);
184 removeAutosaveFile(oldname);
189 void quitLyX(bool noask)
191 lyxerr[Debug::INFO] << "Running QuitLyX." << endl;
193 if (lyx_gui::use_gui) {
194 if (!noask && !theApp->bufferList().quitWriteAll())
197 LyX::cref().session().writeFile();
200 // Set a flag that we do quitting from the program,
201 // so no refreshes are necessary.
204 // close buffers first
205 theApp->bufferList().closeAll();
207 // do any other cleanup procedures now
208 lyxerr[Debug::INFO] << "Deleting tmp dir " << package().temp_dir() << endl;
210 if (!destroyDir(package().temp_dir())) {
211 docstring const msg =
212 bformat(_("Unable to remove the temporary directory %1$s"),
213 lyx::from_utf8(package().temp_dir()));
214 Alert::warning(_("Unable to remove temporary directory"), msg);
223 class AutoSaveBuffer : public ForkedProcess {
226 AutoSaveBuffer(BufferView & bv, string const & fname)
227 : bv_(bv), fname_(fname) {}
229 virtual shared_ptr<ForkedProcess> clone() const
231 return shared_ptr<ForkedProcess>(new AutoSaveBuffer(*this));
237 virtual int generateChild();
244 int AutoSaveBuffer::start()
246 command_ = lyx::to_utf8(bformat(_("Auto-saving %1$s"), lyx::from_utf8(fname_)));
247 return run(DontWait);
251 int AutoSaveBuffer::generateChild()
253 // tmp_ret will be located (usually) in /tmp
254 // will that be a problem?
255 pid_t const pid = fork(); // If you want to debug the autosave
256 // you should set pid to -1, and comment out the
258 if (pid == 0 || pid == -1) {
259 // pid = -1 signifies that lyx was unable
260 // to fork. But we will do the save
264 string const tmp_ret = tempName(string(), "lyxauto");
265 if (!tmp_ret.empty()) {
266 bv_.buffer()->writeFile(tmp_ret);
267 // assume successful write of tmp_ret
268 if (!rename(tmp_ret, fname_)) {
270 // most likely couldn't move between filesystems
271 // unless write of tmp_ret failed
272 // so remove tmp file (if it exists)
280 // failed to write/rename tmp_ret so try writing direct
281 if (!bv_.buffer()->writeFile(fname_)) {
282 // It is dangerous to do this in the child,
283 // but safe in the parent, so...
285 // emit message signal.
286 bv_.buffer()->message(_("Autosave failed!"));
289 if (pid == 0) { // we are the child so...
299 void autoSave(BufferView * bv)
300 // should probably be moved into BufferList (Lgb)
301 // Perfect target for a thread...
306 if (bv->buffer()->isBakClean() || bv->buffer()->isReadonly()) {
307 // We don't save now, but we'll try again later
308 bv->buffer()->resetAutosaveTimers();
312 // emit message signal.
313 bv->buffer()->message(_("Autosaving current document..."));
315 // create autosave filename
316 string fname = bv->buffer()->filePath();
318 fname += onlyFilename(bv->buffer()->fileName());
321 AutoSaveBuffer autosave(*bv, fname);
324 bv->buffer()->markBakClean();
325 bv->buffer()->resetAutosaveTimers();
330 // Copyright CHT Software Service GmbH
333 // create new file with template
336 void newFile(BufferView * bv, string const & filename)
338 // Split argument by :
340 string tmpname = split(filename, name, ':');
341 lyxerr[Debug::INFO] << "Arg is " << filename
342 << "\nName is " << name
343 << "\nTemplate is " << tmpname << endl;
345 Buffer * const b = newFile(name, tmpname);
351 // Insert ascii file (if filename is empty, prompt for one)
352 void insertAsciiFile(BufferView * bv, string const & f, bool asParagraph)
357 // FIXME: We don't know the encoding of the file
358 docstring const tmpstr = lyx::from_utf8(getContentsOfAsciiFile(bv, f, asParagraph));
362 // clear the selection
363 LyXText const & text = bv->buffer()->text();
364 if (&text == bv->getLyXText())
365 bv->cursor().clearSelection();
367 bv->getLyXText()->insertStringAsParagraphs(bv->cursor(), tmpstr);
369 bv->getLyXText()->insertStringAsLines(bv->cursor(), tmpstr);
374 // Insert ascii file (if filename is empty, prompt for one)
375 string getContentsOfAsciiFile(BufferView * bv, string const & f, bool asParagraph)
380 FileDialog fileDlg(lyx::to_utf8(_("Select file to insert")),
381 (asParagraph) ? LFUN_FILE_INSERT_ASCII_PARA : LFUN_FILE_INSERT_ASCII);
383 FileDialog::Result result =
384 fileDlg.open(bv->buffer()->filePath(),
385 FileFilterList(), string());
387 if (result.first == FileDialog::Later)
390 fname = result.second;
396 if (!fs::is_readable(fname)) {
397 docstring const error = lyx::from_ascii(strerror(errno));
398 docstring const file = makeDisplayPath(fname, 50);
399 docstring const text = bformat(_("Could not read the specified document\n"
400 "%1$s\ndue to the error: %2$s"), file, error);
401 Alert::error(_("Could not read file"), text);
405 ifstream ifs(fname.c_str());
407 docstring const error = lyx::from_ascii(strerror(errno));
408 docstring const file = makeDisplayPath(fname, 50);
409 docstring const text = bformat(_("Could not open the specified document\n"
410 "%1$s\ndue to the error: %2$s"), file, error);
411 Alert::error(_("Could not open file"), text);
415 ifs.unsetf(ios::skipws);
416 istream_iterator<char> ii(ifs);
417 istream_iterator<char> end;
418 #if !defined(USE_INCLUDED_STRING) && !defined(STD_STRING_IS_GOOD)
419 // We use this until the compilers get better...
420 std::vector<char> tmp;
421 copy(ii, end, back_inserter(tmp));
422 string const tmpstr(tmp.begin(), tmp.end());
424 // This is what we want to use and what we will use once the
425 // compilers get good enough.
426 //string tmpstr(ii, end); // yet a reason for using std::string
427 // alternate approach to get the file into a string:
429 copy(ii, end, back_inserter(tmpstr));
436 // This function runs "configure" and then rereads lyx.defaults to
437 // reconfigure the automatic settings.
438 void reconfigure(BufferView * bv)
440 // emit message signal.
441 bv->buffer()->message(_("Running configure..."));
443 // Run configure in user lyx directory
444 lyx::support::Path p(package().user_support());
445 string const configure_command = package().configure_command();
447 one.startscript(Systemcall::Wait, configure_command);
449 // emit message signal.
450 bv->buffer()->message(_("Reloading configuration..."));
451 lyxrc.read(libFileSearch(string(), "lyxrc.defaults"));
452 // Re-read packages.lst
453 LaTeXFeatures::getAvailable();
455 Alert::information(_("System reconfigured"),
456 _("The system has been reconfigured.\n"
457 "You need to restart LyX to make use of any\n"
458 "updated document class specifications."));