1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich,
7 * Copyright 1995-2001 The LyX Team.
9 * ====================================================== */
16 #include "bufferlist.h"
17 #include "bufferview_funcs.h"
19 #include "lastfiles.h"
24 #include "BufferView.h"
25 #include "lyxtextclasslist.h"
27 #include "insets/insetlabel.h"
29 #include "frontends/Alert.h"
30 #include "frontends/FileDialog.h"
31 #include "frontends/GUIRunTime.h"
33 #include "support/FileInfo.h"
34 #include "support/filetools.h"
35 #include "support/path.h"
36 #include "support/syscall.h"
37 #include "support/lstrings.h"
49 using std::back_inserter;
50 using std::istream_iterator;
54 extern BufferList bufferlist;
55 // this should be static, but I need it in buffer.C
56 bool quitting; // flag, that we are quitting the program
57 extern bool finished; // all cleanup done just let it run through now.
60 This is the inset locking stuff needed for mathed --------------------
62 an inset can simple call LockInset in it's edit call and *ONLY* in it's
64 Inset::Edit() can only be called by the main lyx module.
66 Then the inset may modify the menu's and/or iconbars.
68 Unlocking is either done by LyX or the inset itself with a UnlockInset-call
70 During the lock, all button and keyboard events will be modified
71 and send to the inset through the following inset-features. Note that
72 Inset::insetUnlock will be called from inside UnlockInset. It is meant
73 to contain the code for restoring the menus and things like this.
76 virtual void insetButtonPress(int x, int y, int button);
77 virtual void insetButtonRelease(int x, int y, int button);
78 virtual void insetKeyPress(XKeyEvent *ev);
79 virtual void insetMotionNotify(int x, int y, int state);
80 virtual void insetUnlock();
82 If a inset wishes any redraw and/or update it just has to call
84 It's is completly irrelevant, where the inset is. UpdateInset will
85 find it in any paragraph in any buffer.
86 Of course the_locking_inset and the insets in the current paragraph/buffer
87 are checked first, so no performance problem should occur.
89 Hope that's ok for the beginning, Alejandro,
90 sorry that I needed so much time,
95 //void UpdateInset(BufferView * bv, Inset * inset, bool mark_dirty = true);
97 /* these functions return 1 if an error occured,
99 // Now they work only for updatable insets. [Alejandro 080596]
100 //int LockInset(UpdatableInset * inset);
101 void ToggleLockedInsetCursor(int x, int y, int asc, int desc);
102 //void FitLockedInsetCursor(long x, long y, int asc, int desc);
103 //int UnlockInset(UpdatableInset * inset);
104 //void LockedInsetStoreUndo(Undo::undo_kind kind);
106 /* this is for asyncron updating. UpdateInsetUpdateList will be called
107 automatically from LyX. Just insert the Inset into the Updatelist */
108 //void UpdateInsetUpdateList();
109 //void PutInsetIntoInsetUpdateList(Inset * inset);
111 //InsetUpdateStruct * InsetUpdateList = 0;
115 -----------------------------------------------------------------------
119 void ShowMessage(Buffer const * buf,
125 && buf && buf->getUser() && buf->getUser()->owner()) {
126 string const str = msg1 + ' ' + msg2 + ' ' + msg3;
127 buf->getUser()->owner()->message(str);
129 lyxerr << msg1 << msg2 << msg3 << endl;
140 // should be moved to lyxfunc.C
141 bool MenuWrite(BufferView * bv, Buffer * buffer)
144 XFlush(GUIRunTime::x11Display());
146 if (!buffer->save()) {
147 if (Alert::askQuestion(_("Save failed. Rename and try again?"),
148 MakeDisplayPath(buffer->fileName(), 50),
149 _("(If not, document is not saved.)"))) {
150 return WriteAs(bv, buffer);
154 lastfiles->newFile(buffer->fileName());
160 // should be moved to BufferView.C
161 // Half of this func should be in LyXView, the rest in BufferView.
162 bool WriteAs(BufferView * bv, Buffer * buffer, string const & filename)
164 string fname = buffer->fileName();
165 string const oldname = fname;
167 if (filename.empty()) {
169 FileDialog fileDlg(bv->owner(),
170 _("Choose a filename to save document as"),
172 make_pair(string(_("Documents|#o#O")),
173 string(lyxrc.document_path)),
174 make_pair(string(_("Templates|#T#t")),
175 string(lyxrc.template_path)));
177 if (!IsLyXFilename(fname))
180 FileDialog::Result result =
181 fileDlg.Select(OnlyPath(fname),
182 _("*.lyx|LyX Documents (*.lyx)"),
183 OnlyFilename(fname));
185 if (result.first == FileDialog::Later)
188 fname = result.second;
193 // Make sure the absolute filename ends with appropriate suffix
194 fname = MakeAbsPath(fname);
195 if (!IsLyXFilename(fname))
200 // Same name as we have already?
201 if (!buffer->isUnnamed() && fname == oldname) {
202 if (!Alert::askQuestion(_("Same name as document already has:"),
203 MakeDisplayPath(fname, 50),
206 // Falls through to name change and save
208 // No, but do we have another file with this name open?
209 else if (!buffer->isUnnamed() && bufferlist.exists(fname)) {
210 if (Alert::askQuestion(_("Another document with same name open!"),
211 MakeDisplayPath(fname, 50),
212 _("Replace with current document?")))
214 bufferlist.close(bufferlist.getBuffer(fname));
216 // Ok, change the name of the buffer, but don't save!
217 buffer->setFileName(fname);
220 ShowMessage(buffer, _("Document renamed to '"),
221 MakeDisplayPath(fname), _("', but not saved..."));
224 } // Check whether the file exists
226 FileInfo const myfile(fname);
227 if (myfile.isOK() && !Alert::askQuestion(_("Document already exists:"),
228 MakeDisplayPath(fname, 50),
233 // Ok, change the name of the buffer
234 buffer->setFileName(fname);
236 bool unnamed = buffer->isUnnamed();
237 buffer->setUnnamed(false);
239 if (!MenuWrite(bv, buffer)) {
240 buffer->setFileName(oldname);
241 buffer->setUnnamed(unnamed);
242 ShowMessage(buffer, _("Document could not be saved!"),
243 _("Holding the old name."), MakeDisplayPath(oldname));
246 // now remove the oldname autosave file if existant!
247 removeAutosaveFile(oldname);
252 int MenuRunChktex(Buffer * buffer)
256 if (buffer->isSGML()) {
257 Alert::alert(_("Chktex does not work with SGML derived documents."));
260 ret = buffer->runChktex();
266 s = _("No warnings found.");
267 } else if (ret == 1) {
268 s = _("One warning found.");
269 t = _("Use `Navigate->Error' to find it.");
272 s += _(" warnings found.");
273 t = _("Use `Navigate->Error' to find them.");
275 Alert::alert(_("Chktex run successfully"), s, t);
277 Alert::alert(_("Error!"), _("It seems chktex does not work."));
285 lyxerr[Debug::INFO] << "Running QuitLyX." << endl;
288 if (!bufferlist.qwriteAll())
291 lastfiles->writeFile(lyxrc.lastfiles);
294 // Set a flag that we do quitting from the program,
295 // so no refreshes are necessary.
298 // close buffers first
299 bufferlist.closeAll();
301 // do any other cleanup procedures now
302 lyxerr[Debug::INFO] << "Deleting tmp dir " << system_tempdir << endl;
304 DestroyLyXTmpDir(system_tempdir);
311 void AutoSave(BufferView * bv)
312 // should probably be moved into BufferList (Lgb)
313 // Perfect target for a thread...
315 if (!bv->available())
318 if (bv->buffer()->isBakClean() || bv->buffer()->isReadonly()) {
319 // We don't save now, but we'll try again later
320 bv->owner()->resetAutosaveTimer();
324 bv->owner()->message(_("Autosaving current document..."));
326 // create autosave filename
327 string fname = bv->buffer()->filePath();
329 fname += OnlyFilename(bv->buffer()->fileName());
332 // tmp_ret will be located (usually) in /tmp
333 // will that be a problem?
334 pid_t const pid = fork(); // If you want to debug the autosave
335 // you should set pid to -1, and comment out the
337 if (pid == 0 || pid == -1) {
338 // pid = -1 signifies that lyx was unable
339 // to fork. But we will do the save
343 string const tmp_ret = lyx::tempName(string(), "lyxauto");
344 if (!tmp_ret.empty()) {
345 bv->buffer()->writeFile(tmp_ret, 1);
346 // assume successful write of tmp_ret
347 if (!lyx::rename(tmp_ret, fname)) {
349 // most likely couldn't move between filesystems
350 // unless write of tmp_ret failed
351 // so remove tmp file (if it exists)
352 lyx::unlink(tmp_ret);
359 // failed to write/rename tmp_ret so try writing direct
360 if (!bv->buffer()->writeFile(fname, 1)) {
361 // It is dangerous to do this in the child,
362 // but safe in the parent, so...
364 bv->owner()->message(_("Autosave failed!"));
367 if (pid == 0) { // we are the child so...
372 bv->buffer()->markBakClean();
373 bv->owner()->resetAutosaveTimer();
378 // Copyright CHT Software Service GmbH
381 // create new file with template
384 Buffer * NewLyxFile(string const & filename)
386 // Split argument by :
388 string tmpname = split(filename, name, ':');
389 #ifdef __EMX__ // Fix me! lyx_cb.C may not be low level enough to allow this.
390 if (name.length() == 1
391 && isalpha(static_cast<unsigned char>(name[0]))
392 && (prefixIs(tmpname, "/") || prefixIs(tmpname, "\\"))) {
394 name += token(tmpname, ':', 0);
395 tmpname = split(tmpname, ':');
398 lyxerr[Debug::INFO] << "Arg is " << filename
399 << "\nName is " << name
400 << "\nTemplate is " << tmpname << endl;
402 // find a free buffer
403 Buffer * tmpbuf = bufferlist.newFile(name, tmpname);
405 lastfiles->newFile(tmpbuf->fileName());
410 // Insert ascii file (if filename is empty, prompt for one)
411 void InsertAsciiFile(BufferView * bv, string const & f, bool asParagraph)
415 if (!bv->available())
419 FileDialog fileDlg(bv->owner(), _("Select file to insert"),
420 (asParagraph) ? LFUN_FILE_INSERT_ASCII_PARA : LFUN_FILE_INSERT_ASCII);
422 FileDialog::Result result = fileDlg.Select(bv->owner()->buffer()->filePath());
424 if (result.first == FileDialog::Later)
427 fname = result.second;
435 if (!fi.readable()) {
436 Alert::err_alert(_("Error! Specified file is unreadable: "),
437 MakeDisplayPath(fname, 50));
441 ifstream ifs(fname.c_str());
443 Alert::err_alert(_("Error! Cannot open specified file: "),
444 MakeDisplayPath(fname, 50));
448 ifs.unsetf(ios::skipws);
449 istream_iterator<char> ii(ifs);
450 istream_iterator<char> end;
451 #if !defined(USE_INCLUDED_STRING) && !defined(STD_STRING_IS_GOOD)
452 // We use this until the compilers get better...
454 copy(ii, end, back_inserter(tmp));
455 string const tmpstr(tmp.begin(), tmp.end());
457 // This is what we want to use and what we will use once the
458 // compilers get good enough.
459 //string tmpstr(ii, end); // yet a reason for using std::string
460 // alternate approach to get the file into a string:
462 copy(ii, end, back_inserter(tmpstr));
467 // clear the selection
468 bv->beforeChange(bv->text);
470 bv->text->insertStringAsLines(bv, tmpstr);
472 bv->text->insertStringAsParagraphs(bv, tmpstr);
473 bv->update(bv->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
477 void MenuInsertLabel(BufferView * bv, string const & arg)
480 bv->owner()->prohibitInput();
482 Paragraph * par = bv->getLyXText()->cursor.par();
483 LyXLayout const * layout =
484 &textclasslist.Style(bv->buffer()->params.textclass,
487 if (layout->latextype == LATEX_PARAGRAPH && par->previous()) {
488 Paragraph * par2 = par->previous();
489 LyXLayout const * layout2 =
490 &textclasslist.Style(bv->buffer()->params.textclass,
492 if (layout2->latextype != LATEX_PARAGRAPH) {
497 string text = layout->latexname().substr(0, 3);
498 if (layout->latexname() == "theorem")
499 text = "thm"; // Create a correct prefix for prettyref
502 if (layout->latextype == LATEX_PARAGRAPH ||
503 lyxrc.label_init_length < 0)
505 string par_text = par->asString(bv->buffer(), false);
506 for (int i = 0; i < lyxrc.label_init_length; ++i) {
507 if (par_text.empty())
510 par_text = split(par_text, head, ' ');
512 text += '-'; // Is it legal to use spaces in
517 pair<bool, string> result =
518 Alert::askForText(_("Enter new label to insert:"), text);
520 label = frontStrip(strip(result.second));
523 if (!label.empty()) {
524 InsetCommandParams p( "label", label );
525 InsetLabel * inset = new InsetLabel( p );
526 bv->insertInset( inset );
528 bv->owner()->allowInput();
532 // This function runs "configure" and then rereads lyx.defaults to
533 // reconfigure the automatic settings.
534 void Reconfigure(BufferView * bv)
536 bv->owner()->message(_("Running configure..."));
538 // Run configure in user lyx directory
540 Systemcalls one(Systemcalls::System,
541 AddName(system_lyxdir, "configure"));
543 bv->owner()->message(_("Reloading configuration..."));
544 lyxrc.read(LibFileSearch(string(), "lyxrc.defaults"));
545 Alert::alert(_("The system has been reconfigured."),
546 _("You need to restart LyX to make use of any"),
547 _("updated document class specifications."));