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 string const str = msg1 + ' ' + msg2 + ' ' + msg3;
126 buf->getUser()->owner()->message(str);
128 lyxerr << msg1 << msg2 << msg3 << endl;
139 // should be moved to lyxfunc.C
140 bool MenuWrite(BufferView * bv, Buffer * buffer)
143 XFlush(GUIRunTime::x11Display());
145 if (!buffer->save()) {
146 string const fname = buffer->fileName();
147 string const s = MakeAbsPath(fname);
148 if (Alert::askQuestion(_("Save failed. Rename and try again?"),
149 MakeDisplayPath(s, 50),
150 _("(If not, document is not saved.)"))) {
151 return WriteAs(bv, buffer);
155 lastfiles->newFile(buffer->fileName());
161 // should be moved to BufferView.C
162 // Half of this func should be in LyXView, the rest in BufferView.
163 bool WriteAs(BufferView * bv, Buffer * buffer, string const & filename)
165 string fname = buffer->fileName();
166 string const oldname = fname;
168 if (filename.empty()) {
170 FileDialog fileDlg(bv->owner(),
171 _("Choose a filename to save document as"),
173 make_pair(string(_("Documents")),
174 string(lyxrc.document_path)),
175 make_pair(string(_("Templates")),
176 string(lyxrc.template_path)));
178 if (!IsLyXFilename(fname))
181 FileDialog::Result result =
182 fileDlg.Select(OnlyPath(fname),
183 _("*.lyx|LyX Documents (*.lyx)"),
184 OnlyFilename(fname));
186 if (result.first == FileDialog::Later)
189 fname = result.second;
194 // Make sure the absolute filename ends with appropriate suffix
195 fname = MakeAbsPath(fname);
196 if (!IsLyXFilename(fname))
201 // Same name as we have already?
202 if (!buffer->isUnnamed() && fname == oldname) {
203 if (!Alert::askQuestion(_("Same name as document already has:"),
204 MakeDisplayPath(fname, 50),
207 // Falls through to name change and save
209 // No, but do we have another file with this name open?
210 else if (!buffer->isUnnamed() && bufferlist.exists(fname)) {
211 if (Alert::askQuestion(_("Another document with same name open!"),
212 MakeDisplayPath(fname, 50),
213 _("Replace with current document?")))
215 bufferlist.close(bufferlist.getBuffer(fname));
217 // Ok, change the name of the buffer, but don't save!
218 buffer->setFileName(fname);
221 ShowMessage(buffer, _("Document renamed to '"),
222 MakeDisplayPath(fname), _("', but not saved..."));
225 } // Check whether the file exists
227 FileInfo const myfile(fname);
228 if (myfile.isOK() && !Alert::askQuestion(_("Document already exists:"),
229 MakeDisplayPath(fname, 50),
234 // Ok, change the name of the buffer
235 buffer->setFileName(fname);
237 bool unnamed = buffer->isUnnamed();
238 buffer->setUnnamed(false);
240 if (!MenuWrite(bv, buffer)) {
241 buffer->setFileName(oldname);
242 buffer->setUnnamed(unnamed);
243 ShowMessage(buffer, _("Document could not be saved!"),
244 _("Holding the old name."), MakeDisplayPath(oldname));
247 // now remove the oldname autosave file if existant!
248 removeAutosaveFile(oldname);
253 int MenuRunChktex(Buffer * buffer)
257 if (buffer->isSGML()) {
258 Alert::alert(_("Chktex does not work with SGML derived documents."));
261 ret = buffer->runChktex();
267 s = _("No warnings found.");
268 } else if (ret == 1) {
269 s = _("One warning found.");
270 t = _("Use `Navigate->Error' to find it.");
273 s += _(" warnings found.");
274 t = _("Use `Navigate->Error' to find them.");
276 Alert::alert(_("Chktex run successfully"), s, t);
278 Alert::alert(_("Error!"), _("It seems chktex does not work."));
286 lyxerr[Debug::INFO] << "Running QuitLyX." << endl;
289 if (!bufferlist.qwriteAll())
292 lastfiles->writeFile(lyxrc.lastfiles);
295 // Set a flag that we do quitting from the program,
296 // so no refreshes are necessary.
299 // close buffers first
300 bufferlist.closeAll();
302 // do any other cleanup procedures now
303 lyxerr[Debug::INFO] << "Deleting tmp dir " << system_tempdir << endl;
305 DestroyLyXTmpDir(system_tempdir);
312 void AutoSave(BufferView * bv)
313 // should probably be moved into BufferList (Lgb)
314 // Perfect target for a thread...
316 if (!bv->available())
319 if (bv->buffer()->isBakClean() || bv->buffer()->isReadonly()) {
320 // We don't save now, but we'll try again later
321 bv->owner()->resetAutosaveTimer();
325 bv->owner()->message(_("Autosaving current document..."));
327 // create autosave filename
328 string fname = OnlyPath(bv->buffer()->fileName());
330 fname += OnlyFilename(bv->buffer()->fileName());
333 // tmp_ret will be located (usually) in /tmp
334 // will that be a problem?
335 pid_t const pid = fork(); // If you want to debug the autosave
336 // you should set pid to -1, and comment out the
338 if (pid == 0 || pid == -1) {
339 // pid = -1 signifies that lyx was unable
340 // to fork. But we will do the save
344 string const tmp_ret = lyx::tempName(string(), "lyxauto");
345 if (!tmp_ret.empty()) {
346 bv->buffer()->writeFile(tmp_ret, 1);
347 // assume successful write of tmp_ret
348 if (!lyx::rename(tmp_ret, fname)) {
350 // most likely couldn't move between filesystems
351 // unless write of tmp_ret failed
352 // so remove tmp file (if it exists)
353 lyx::unlink(tmp_ret);
360 // failed to write/rename tmp_ret so try writing direct
361 if (!bv->buffer()->writeFile(fname, 1)) {
362 // It is dangerous to do this in the child,
363 // but safe in the parent, so...
365 bv->owner()->message(_("Autosave failed!"));
368 if (pid == 0) { // we are the child so...
373 bv->buffer()->markBakClean();
374 bv->owner()->resetAutosaveTimer();
379 // Copyright CHT Software Service GmbH
382 // create new file with template
385 Buffer * NewLyxFile(string const & filename)
387 // Split argument by :
389 string tmpname = split(filename, name, ':');
390 #ifdef __EMX__ // Fix me! lyx_cb.C may not be low level enough to allow this.
391 if (name.length() == 1
392 && isalpha(static_cast<unsigned char>(name[0]))
393 && (prefixIs(tmpname, "/") || prefixIs(tmpname, "\\"))) {
395 name += token(tmpname, ':', 0);
396 tmpname = split(tmpname, ':');
399 lyxerr[Debug::INFO] << "Arg is " << filename
400 << "\nName is " << name
401 << "\nTemplate is " << tmpname << endl;
403 // find a free buffer
404 Buffer * tmpbuf = bufferlist.newFile(name, tmpname);
406 lastfiles->newFile(tmpbuf->fileName());
411 // Insert ascii file (if filename is empty, prompt for one)
412 void InsertAsciiFile(BufferView * bv, string const & f, bool asParagraph)
416 if (!bv->available())
420 FileDialog fileDlg(bv->owner(), _("Select file to insert"),
421 (asParagraph) ? LFUN_FILE_INSERT_ASCII_PARA : LFUN_FILE_INSERT_ASCII);
423 FileDialog::Result result = fileDlg.Select(bv->owner()->buffer()->filepath);
425 if (result.first == FileDialog::Later)
428 fname = result.second;
436 if (!fi.readable()) {
437 Alert::err_alert(_("Error! Specified file is unreadable: "),
438 MakeDisplayPath(fname, 50));
442 ifstream ifs(fname.c_str());
444 Alert::err_alert(_("Error! Cannot open specified file: "),
445 MakeDisplayPath(fname, 50));
449 ifs.unsetf(ios::skipws);
450 istream_iterator<char> ii(ifs);
451 istream_iterator<char> end;
452 #if !defined(USE_INCLUDED_STRING) && !defined(STD_STRING_IS_GOOD)
453 // We use this until the compilers get better...
455 copy(ii, end, back_inserter(tmp));
456 string const tmpstr(tmp.begin(), tmp.end());
458 // This is what we want to use and what we will use once the
459 // compilers get good enough.
460 //string tmpstr(ii, end); // yet a reason for using std::string
461 // alternate approach to get the file into a string:
463 copy(ii, end, back_inserter(tmpstr));
468 // clear the selection
469 bv->beforeChange(bv->text);
471 bv->text->insertStringAsLines(bv, tmpstr);
473 bv->text->insertStringAsParagraphs(bv, tmpstr);
474 bv->update(bv->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
478 void MenuInsertLabel(BufferView * bv, string const & arg)
481 bv->owner()->prohibitInput();
483 Paragraph * par = bv->getLyXText()->cursor.par();
484 LyXLayout const * layout =
485 &textclasslist.Style(bv->buffer()->params.textclass,
488 if (layout->latextype == LATEX_PARAGRAPH && par->previous()) {
489 Paragraph * par2 = par->previous();
490 LyXLayout const * layout2 =
491 &textclasslist.Style(bv->buffer()->params.textclass,
493 if (layout2->latextype != LATEX_PARAGRAPH) {
498 string text = layout->latexname().substr(0, 3);
499 if (layout->latexname() == "theorem")
500 text = "thm"; // Create a correct prefix for prettyref
503 if (layout->latextype == LATEX_PARAGRAPH ||
504 lyxrc.label_init_length < 0)
506 string par_text = par->asString(bv->buffer(), false);
507 for (int i = 0; i < lyxrc.label_init_length; ++i) {
508 if (par_text.empty())
511 par_text = split(par_text, head, ' ');
513 text += '-'; // Is it legal to use spaces in
518 pair<bool, string> result =
519 Alert::askForText(_("Enter new label to insert:"), text);
521 label = frontStrip(strip(result.second));
524 if (!label.empty()) {
525 InsetCommandParams p( "label", label );
526 InsetLabel * inset = new InsetLabel( p );
527 bv->insertInset( inset );
529 bv->owner()->allowInput();
533 // This function runs "configure" and then rereads lyx.defaults to
534 // reconfigure the automatic settings.
535 void Reconfigure(BufferView * bv)
537 bv->owner()->message(_("Running configure..."));
539 // Run configure in user lyx directory
541 Systemcalls one(Systemcalls::System,
542 AddName(system_lyxdir, "configure"));
544 bv->owner()->message(_("Reloading configuration..."));
545 lyxrc.read(LibFileSearch(string(), "lyxrc.defaults"));
546 Alert::alert(_("The system has been reconfigured."),
547 _("You need to restart LyX to make use of any"),
548 _("updated document class specifications."));