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"
24 #include "lastfiles.h"
26 #include "lyxlayout.h"
29 #include "paragraph.h"
31 #include "frontends/Alert.h"
32 #include "frontends/FileDialog.h"
33 #include "frontends/lyx_gui.h"
34 #include "frontends/LyXView.h"
36 #include "support/FileInfo.h"
37 #include "support/filetools.h"
38 #include "support/forkedcall.h"
39 #include "support/globbing.h"
40 #include "support/lyxlib.h"
41 #include "support/os.h"
42 #include "support/path.h"
43 #include "support/path_defines.h"
44 #include "support/systemcall.h"
49 using lyx::support::AddName;
50 using lyx::support::bformat;
51 using lyx::support::destroyDir;
52 using lyx::support::FileFilterList;
53 using lyx::support::FileInfo;
54 using lyx::support::ForkedProcess;
55 using lyx::support::IsLyXFilename;
56 using lyx::support::LibFileSearch;
57 using lyx::support::MakeAbsPath;
58 using lyx::support::MakeDisplayPath;
59 using lyx::support::OnlyFilename;
60 using lyx::support::OnlyPath;
61 using lyx::support::Path;
62 using lyx::support::removeAutosaveFile;
63 using lyx::support::rename;
64 using lyx::support::split;
65 using lyx::support::system_lyxdir;
66 using lyx::support::Systemcall;
67 using lyx::support::tempName;
68 using lyx::support::unlink;
69 using lyx::support::user_lyxdir;
71 namespace os = lyx::support::os;
74 using std::back_inserter;
81 using std::istream_iterator;
84 extern BufferList bufferlist;
85 // this should be static, but I need it in buffer.C
86 bool quitting; // flag, that we are quitting the program
93 bool MenuWrite(Buffer * buffer)
96 LyX::ref().lastfiles().newFile(buffer->fileName());
100 // FIXME: we don't tell the user *WHY* the save failed !!
102 string const file = MakeDisplayPath(buffer->fileName(), 30);
104 string text = bformat(_("The document %1$s could not be saved.\n\n"
105 "Do you want to rename the document and try again?"), file);
106 int const ret = Alert::prompt(_("Rename and save?"),
107 text, 0, 1, _("&Rename"), _("&Cancel"));
110 return WriteAs(buffer);
116 bool WriteAs(Buffer * buffer, string const & filename)
118 string fname = buffer->fileName();
119 string const oldname = fname;
121 if (filename.empty()) {
123 FileDialog fileDlg(_("Choose a filename to save document as"),
125 make_pair(string(_("Documents|#o#O")),
126 string(lyxrc.document_path)),
127 make_pair(string(_("Templates|#T#t")),
128 string(lyxrc.template_path)));
130 if (!IsLyXFilename(fname))
133 FileFilterList const filter (_("LyX Documents (*.lyx)"));
135 FileDialog::Result result =
136 fileDlg.save(OnlyPath(fname),
138 OnlyFilename(fname));
140 if (result.first == FileDialog::Later)
143 fname = result.second;
148 // Make sure the absolute filename ends with appropriate suffix
149 fname = MakeAbsPath(fname);
150 if (!IsLyXFilename(fname))
155 FileInfo const myfile(fname);
157 string const file = MakeDisplayPath(fname, 30);
158 string text = bformat(_("The document %1$s already exists.\n\n"
159 "Do you want to over-write that document?"), file);
160 int const ret = Alert::prompt(_("Over-write document?"),
161 text, 0, 1, _("&Over-write"), _("&Cancel"));
167 // Ok, change the name of the buffer
168 buffer->setFileName(fname);
170 bool unnamed = buffer->isUnnamed();
171 buffer->setUnnamed(false);
173 if (!MenuWrite(buffer)) {
174 buffer->setFileName(oldname);
175 buffer->setUnnamed(unnamed);
179 removeAutosaveFile(oldname);
186 lyxerr[Debug::INFO] << "Running QuitLyX." << endl;
188 if (lyx_gui::use_gui) {
189 if (!bufferlist.quitWriteAll())
192 LyX::cref().lastfiles().writeFile(lyxrc.lastfiles);
195 // Set a flag that we do quitting from the program,
196 // so no refreshes are necessary.
199 // close buffers first
200 bufferlist.closeAll();
202 // do any other cleanup procedures now
203 lyxerr[Debug::INFO] << "Deleting tmp dir " << os::getTmpDir() << endl;
205 if (destroyDir(os::getTmpDir()) != 0) {
206 string msg = bformat(_("Could not remove the temporary directory %1$s"),
208 Alert::warning(_("Could not remove temporary directory"), msg);
217 class AutoSaveBuffer : public ForkedProcess {
220 AutoSaveBuffer(BufferView & bv, string const & fname)
221 : bv_(bv), fname_(fname) {}
223 virtual auto_ptr<ForkedProcess> clone() const {
224 return auto_ptr<ForkedProcess>(new AutoSaveBuffer(*this));
230 virtual int generateChild();
237 int AutoSaveBuffer::start()
239 command_ = bformat(_("Auto-saving %1$s"), fname_);
240 return runNonBlocking();
244 int AutoSaveBuffer::generateChild()
246 // tmp_ret will be located (usually) in /tmp
247 // will that be a problem?
248 pid_t const pid = fork(); // If you want to debug the autosave
249 // you should set pid to -1, and comment out the
251 if (pid == 0 || pid == -1) {
252 // pid = -1 signifies that lyx was unable
253 // to fork. But we will do the save
257 string const tmp_ret = tempName(string(), "lyxauto");
258 if (!tmp_ret.empty()) {
259 bv_.buffer()->writeFile(tmp_ret);
260 // assume successful write of tmp_ret
261 if (!rename(tmp_ret, fname_)) {
263 // most likely couldn't move between filesystems
264 // unless write of tmp_ret failed
265 // so remove tmp file (if it exists)
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...
278 bv_.owner()->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->available())
298 if (bv->buffer()->isBakClean() || bv->buffer()->isReadonly()) {
299 // We don't save now, but we'll try again later
300 bv->owner()->resetAutosaveTimer();
304 bv->owner()->message(_("Autosaving current document..."));
306 // create autosave filename
307 string fname = bv->buffer()->filePath();
309 fname += OnlyFilename(bv->buffer()->fileName());
312 AutoSaveBuffer autosave(*bv, fname);
315 bv->buffer()->markBakClean();
316 bv->owner()->resetAutosaveTimer();
321 // Copyright CHT Software Service GmbH
324 // create new file with template
327 void NewFile(BufferView * bv, string const & filename)
329 // Split argument by :
331 string tmpname = split(filename, name, ':');
332 #ifdef __EMX__ // Fix me! lyx_cb.C may not be low level enough to allow this.
333 if (name.length() == 1
334 && isalpha(static_cast<unsigned char>(name[0]))
335 && (prefixIs(tmpname, "/") || prefixIs(tmpname, "\\"))) {
337 name += token(tmpname, ':', 0);
338 tmpname = split(tmpname, ':');
341 lyxerr[Debug::INFO] << "Arg is " << filename
342 << "\nName is " << name
343 << "\nTemplate is " << tmpname << endl;
345 bv->newFile(name, tmpname);
349 // Insert ascii file (if filename is empty, prompt for one)
350 void InsertAsciiFile(BufferView * bv, string const & f, bool asParagraph)
352 if (!bv->available())
355 string const tmpstr = getContentsOfAsciiFile(bv, f, asParagraph);
359 // clear the selection
360 if (bv->text() == bv->getLyXText())
361 bv->clearSelection();
363 bv->getLyXText()->insertStringAsParagraphs(tmpstr);
365 bv->getLyXText()->insertStringAsLines(tmpstr);
370 // Insert ascii file (if filename is empty, prompt for one)
371 string getContentsOfAsciiFile(BufferView * bv, string const & f, bool asParagraph)
376 FileDialog fileDlg(_("Select file to insert"),
377 (asParagraph) ? LFUN_FILE_INSERT_ASCII_PARA : LFUN_FILE_INSERT_ASCII);
379 FileDialog::Result result =
380 fileDlg.open(bv->owner()->buffer()->filePath(),
381 FileFilterList(), string());
383 if (result.first == FileDialog::Later)
386 fname = result.second;
394 if (!fi.readable()) {
395 string const error = strerror(errno);
396 string const file = MakeDisplayPath(fname, 50);
397 string const text = bformat(_("Could not read the specified document\n"
398 "%1$s\ndue to the error: %2$s"), file, error);
399 Alert::error(_("Could not read file"), text);
403 ifstream ifs(fname.c_str());
405 string const error = strerror(errno);
406 string const file = MakeDisplayPath(fname, 50);
407 string const text = bformat(_("Could not open the specified document\n"
408 "%1$s\ndue to the error: %2$s"), file, error);
409 Alert::error(_("Could not open file"), text);
413 ifs.unsetf(ios::skipws);
414 istream_iterator<char> ii(ifs);
415 istream_iterator<char> end;
416 #if !defined(USE_INCLUDED_STRING) && !defined(STD_STRING_IS_GOOD)
417 // We use this until the compilers get better...
418 std::vector<char> tmp;
419 copy(ii, end, back_inserter(tmp));
420 string const tmpstr(tmp.begin(), tmp.end());
422 // This is what we want to use and what we will use once the
423 // compilers get good enough.
424 //string tmpstr(ii, end); // yet a reason for using std::string
425 // alternate approach to get the file into a string:
427 copy(ii, end, back_inserter(tmpstr));
434 string const getPossibleLabel(BufferView const & bv)
436 ParagraphList::iterator pit = bv.getLyXText()->cursorPar();
437 ParagraphList & plist = bv.getLyXText()->paragraphs();
439 LyXLayout_ptr layout = pit->layout();
441 if (layout->latextype == LATEX_PARAGRAPH && pit != plist.begin()) {
442 ParagraphList::iterator pit2 = boost::prior(pit);
444 LyXLayout_ptr const & layout2 = pit2->layout();
446 if (layout2->latextype != LATEX_PARAGRAPH) {
452 string text = layout->latexname().substr(0, 3);
453 if (layout->latexname() == "theorem")
454 text = "thm"; // Create a correct prefix for prettyref
457 if (layout->latextype == LATEX_PARAGRAPH ||
458 lyxrc.label_init_length < 0)
461 string par_text = pit->asString(*bv.buffer(), false);
462 for (int i = 0; i < lyxrc.label_init_length; ++i) {
463 if (par_text.empty())
466 par_text = split(par_text, head, ' ');
467 // Is it legal to use spaces in labels ?
477 // This function runs "configure" and then rereads lyx.defaults to
478 // reconfigure the automatic settings.
479 void Reconfigure(BufferView * bv)
481 bv->owner()->message(_("Running configure..."));
483 // Run configure in user lyx directory
484 Path p(user_lyxdir());
486 one.startscript(Systemcall::Wait,
487 AddName(system_lyxdir(), "configure"));
489 bv->owner()->message(_("Reloading configuration..."));
490 lyxrc.read(LibFileSearch(string(), "lyxrc.defaults"));
492 Alert::information(_("System reconfigured"),
493 _("The system has been reconfigured.\n"
494 "You need to restart LyX to make use of any \n"
495 "updated document class specifications."));