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 * ====================================================== */
19 #include "lyx_gui_misc.h"
22 #include "bufferlist.h"
23 #include "bufferview_funcs.h"
25 #include "lastfiles.h"
29 #include "frontends/FileDialog.h"
30 #include "frontends/GUIRunTime.h"
31 #include "insets/insetlabel.h"
32 #include "support/FileInfo.h"
33 #include "support/filetools.h"
34 #include "support/path.h"
35 #include "support/syscall.h"
36 #include "support/lstrings.h"
38 #include "BufferView.h"
45 using std::back_inserter;
46 using std::istream_iterator;
50 extern BufferList bufferlist;
51 // this should be static, but I need it in buffer.C
52 bool quitting; // flag, that we are quitting the program
53 extern bool finished; // all cleanup done just let it run through now.
56 This is the inset locking stuff needed for mathed --------------------
58 an inset can simple call LockInset in it's edit call and *ONLY* in it's
60 Inset::Edit() can only be called by the main lyx module.
62 Then the inset may modify the menu's and/or iconbars.
64 Unlocking is either done by LyX or the inset itself with a UnlockInset-call
66 During the lock, all button and keyboard events will be modified
67 and send to the inset through the following inset-features. Note that
68 Inset::insetUnlock will be called from inside UnlockInset. It is meant
69 to contain the code for restoring the menus and things like this.
72 virtual void insetButtonPress(int x, int y, int button);
73 virtual void insetButtonRelease(int x, int y, int button);
74 virtual void insetKeyPress(XKeyEvent *ev);
75 virtual void insetMotionNotify(int x, int y, int state);
76 virtual void insetUnlock();
78 If a inset wishes any redraw and/or update it just has to call
80 It's is completly irrelevant, where the inset is. UpdateInset will
81 find it in any paragraph in any buffer.
82 Of course the_locking_inset and the insets in the current paragraph/buffer
83 are checked first, so no performance problem should occur.
85 Hope that's ok for the beginning, Alejandro,
86 sorry that I needed so much time,
91 //void UpdateInset(BufferView * bv, Inset * inset, bool mark_dirty = true);
93 /* these functions return 1 if an error occured,
95 // Now they work only for updatable insets. [Alejandro 080596]
96 //int LockInset(UpdatableInset * inset);
97 void ToggleLockedInsetCursor(int x, int y, int asc, int desc);
98 //void FitLockedInsetCursor(long x, long y, int asc, int desc);
99 //int UnlockInset(UpdatableInset * inset);
100 //void LockedInsetStoreUndo(Undo::undo_kind kind);
102 /* this is for asyncron updating. UpdateInsetUpdateList will be called
103 automatically from LyX. Just insert the Inset into the Updatelist */
104 //void UpdateInsetUpdateList();
105 //void PutInsetIntoInsetUpdateList(Inset * inset);
107 //InsetUpdateStruct * InsetUpdateList = 0;
111 -----------------------------------------------------------------------
115 void ShowMessage(Buffer const * buf,
121 string const str = msg1 + ' ' + msg2 + ' ' + msg3;
122 buf->getUser()->owner()->message(str);
124 lyxerr << msg1 << msg2 << msg3 << endl;
135 // should be moved to lyxfunc.C
136 bool MenuWrite(BufferView * bv, Buffer * buffer)
139 XFlush(GUIRunTime::x11Display());
141 if (!buffer->save()) {
142 string const fname = buffer->fileName();
143 string const s = MakeAbsPath(fname);
144 if (AskQuestion(_("Save failed. Rename and try again?"),
145 MakeDisplayPath(s, 50),
146 _("(If not, document is not saved.)"))) {
147 return WriteAs(bv, buffer);
151 lastfiles->newFile(buffer->fileName());
157 // should be moved to BufferView.C
158 // Half of this func should be in LyXView, the rest in BufferView.
159 bool WriteAs(BufferView * bv, Buffer * buffer, string const & filename)
161 string fname = buffer->fileName();
162 string oldname = fname;
164 if (filename.empty()) {
166 FileDialog fileDlg(bv->owner(),
167 _("Choose a filename to save document as"),
169 make_pair(string(_("Documents")),
170 string(lyxrc.document_path)),
171 make_pair(string(_("Templates")),
172 string(lyxrc.template_path)));
174 if (!IsLyXFilename(fname))
177 FileDialog::Result result =
178 fileDlg.Select(OnlyPath(fname),
179 _("*.lyx|LyX Documents (*.lyx)"),
180 OnlyFilename(fname));
182 if (result.first == FileDialog::Later)
185 fname = result.second;
190 // Make sure the absolute filename ends with appropriate suffix
191 fname = MakeAbsPath(fname);
192 if (!IsLyXFilename(fname))
197 // Same name as we have already?
198 if (!buffer->isUnnamed() && fname == oldname) {
199 if (!AskQuestion(_("Same name as document already has:"),
200 MakeDisplayPath(fname, 50),
203 // Falls through to name change and save
205 // No, but do we have another file with this name open?
206 else if (!buffer->isUnnamed() && bufferlist.exists(fname)) {
207 if (AskQuestion(_("Another document with same name open!"),
208 MakeDisplayPath(fname, 50),
209 _("Replace with current document?")))
211 bufferlist.close(bufferlist.getBuffer(fname));
213 // Ok, change the name of the buffer, but don't save!
214 buffer->setFileName(fname);
217 ShowMessage(buffer, _("Document renamed to '"),
218 MakeDisplayPath(fname), _("', but not saved..."));
221 } // Check whether the file exists
223 FileInfo const myfile(fname);
224 if (myfile.isOK() && !AskQuestion(_("Document already exists:"),
225 MakeDisplayPath(fname, 50),
230 // Ok, change the name of the buffer
231 buffer->setFileName(fname);
233 bool unnamed = buffer->isUnnamed();
234 buffer->setUnnamed(false);
236 if (!MenuWrite(bv, buffer)) {
237 buffer->setFileName(oldname);
238 buffer->setUnnamed(unnamed);
239 ShowMessage(buffer, _("Document could not be saved!"),
240 _("Holding the old name."), MakeDisplayPath(oldname));
243 // now remove the oldname autosave file if existant!
244 removeAutosaveFile(oldname);
249 int MenuRunChktex(Buffer * buffer)
253 if (buffer->isSGML()) {
254 WriteAlert(_("Chktex does not work with SGML derived documents."));
257 ret = buffer->runChktex();
263 s = _("No warnings found.");
264 } else if (ret == 1) {
265 s = _("One warning found.");
266 t = _("Use 'Edit->Go to Error' to find it.");
269 s += _(" warnings found.");
270 t = _("Use 'Edit->Go to Error' to find them.");
272 WriteAlert(_("Chktex run successfully"), s, t);
274 WriteAlert(_("Error!"), _("It seems chktex does not work."));
282 lyxerr[Debug::INFO] << "Running QuitLyX." << endl;
285 if (!bufferlist.qwriteAll())
288 lastfiles->writeFile(lyxrc.lastfiles);
291 // Set a flag that we do quitting from the program,
292 // so no refreshes are necessary.
295 // close buffers first
296 bufferlist.closeAll();
298 // do any other cleanup procedures now
299 lyxerr[Debug::INFO] << "Deleting tmp dir " << system_tempdir << endl;
301 DestroyLyXTmpDir(system_tempdir);
308 void AutoSave(BufferView * bv)
309 // should probably be moved into BufferList (Lgb)
310 // Perfect target for a thread...
312 if (!bv->available())
315 if (bv->buffer()->isBakClean() || bv->buffer()->isReadonly()) {
316 // We don't save now, but we'll try again later
317 bv->owner()->resetAutosaveTimer();
321 bv->owner()->message(_("Autosaving current document..."));
323 // create autosave filename
324 string fname = OnlyPath(bv->buffer()->fileName());
326 fname += OnlyFilename(bv->buffer()->fileName());
329 // tmp_ret will be located (usually) in /tmp
330 // will that be a problem?
331 pid_t const pid = fork(); // If you want to debug the autosave
332 // you should set pid to -1, and comment out the
334 if (pid == 0 || pid == -1) {
335 // pid = -1 signifies that lyx was unable
336 // to fork. But we will do the save
340 string const tmp_ret = lyx::tempName(string(), "lyxauto");
341 if (!tmp_ret.empty()) {
342 bv->buffer()->writeFile(tmp_ret, 1);
343 // assume successful write of tmp_ret
344 if (!lyx::rename(tmp_ret, fname)) {
346 // most likely couldn't move between filesystems
347 // unless write of tmp_ret failed
348 // so remove tmp file (if it exists)
349 lyx::unlink(tmp_ret);
356 // failed to write/rename tmp_ret so try writing direct
357 if (!bv->buffer()->writeFile(fname, 1)) {
358 // It is dangerous to do this in the child,
359 // but safe in the parent, so...
361 bv->owner()->message(_("Autosave Failed!"));
364 if (pid == 0) { // we are the child so...
369 bv->buffer()->markBakClean();
370 bv->owner()->resetAutosaveTimer();
375 // Copyright CHT Software Service GmbH
378 // create new file with template
381 Buffer * NewLyxFile(string const & filename)
383 // Split argument by :
385 string tmpname = split(filename, name, ':');
386 #ifdef __EMX__ // Fix me! lyx_cb.C may not be low level enough to allow this.
387 if (name.length() == 1
388 && isalpha(static_cast<unsigned char>(name[0]))
389 && (prefixIs(tmpname, "/") || prefixIs(tmpname, "\\"))) {
391 name += token(tmpname, ':', 0);
392 tmpname = split(tmpname, ':');
395 lyxerr[Debug::INFO] << "Arg is " << filename
396 << "\nName is " << name
397 << "\nTemplate is " << tmpname << endl;
399 // find a free buffer
400 Buffer * tmpbuf = bufferlist.newFile(name, tmpname);
402 lastfiles->newFile(tmpbuf->fileName());
407 // Insert ascii file (if filename is empty, prompt for one)
408 void InsertAsciiFile(BufferView * bv, string const & f, bool asParagraph)
412 if (!bv->available())
416 FileDialog fileDlg(bv->owner(), _("Select file to insert"),
417 (asParagraph) ? LFUN_FILE_INSERT_ASCII_PARA : LFUN_FILE_INSERT_ASCII);
419 FileDialog::Result result = fileDlg.Select(bv->owner()->buffer()->filepath);
421 if (result.first == FileDialog::Later)
424 fname = result.second;
432 if (!fi.readable()) {
433 WriteFSAlert(_("Error! Specified file is unreadable: "),
434 MakeDisplayPath(fname, 50));
438 ifstream ifs(fname.c_str());
440 WriteFSAlert(_("Error! Cannot open specified file: "),
441 MakeDisplayPath(fname, 50));
445 ifs.unsetf(ios::skipws);
446 istream_iterator<char> ii(ifs);
447 istream_iterator<char> end;
448 #if !defined(USE_INCLUDED_STRING) && !defined(STD_STRING_IS_GOOD)
449 // We use this until the compilers get better...
451 copy(ii, end, back_inserter(tmp));
452 string const tmpstr(tmp.begin(), tmp.end());
454 // This is what we want to use and what we will use once the
455 // compilers get good enough.
456 //string tmpstr(ii, end); // yet a reason for using std::string
457 // alternate approach to get the file into a string:
459 copy(ii, end, back_inserter(tmpstr));
464 // clear the selection
465 bv->beforeChange(bv->text);
467 bv->text->insertStringAsLines(bv, tmpstr);
469 bv->text->insertStringAsParagraphs(bv, tmpstr);
470 bv->update(bv->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
474 void MenuInsertLabel(BufferView * bv, string const & arg)
477 bv->owner()->prohibitInput();
479 Paragraph * par = bv->text->cursor.par();
480 LyXLayout const * layout =
481 &textclasslist.Style(bv->buffer()->params.textclass,
484 if (layout->latextype == LATEX_PARAGRAPH && par->previous()) {
485 Paragraph * par2 = par->previous();
486 LyXLayout const * layout2 =
487 &textclasslist.Style(bv->buffer()->params.textclass,
489 if (layout2->latextype != LATEX_PARAGRAPH) {
494 string text = layout->latexname().substr(0, 3);
495 if (layout->latexname() == "theorem")
496 text = "thm"; // Create a correct prefix for prettyref
499 if (layout->latextype == LATEX_PARAGRAPH ||
500 lyxrc.label_init_length < 0)
502 string par_text = par->asString(bv->buffer(), false);
503 for (int i = 0; i < lyxrc.label_init_length; ++i) {
504 if (par_text.empty())
507 par_text = split(par_text, head, ' ');
509 text += '-'; // Is it legal to use spaces in
514 pair<bool, string> result =
515 askForText(_("Enter new label to insert:"), text);
517 label = frontStrip(strip(result.second));
520 if (!label.empty()) {
521 InsetCommandParams p( "label", label );
522 InsetLabel * inset = new InsetLabel( p );
523 bv->insertInset( inset );
525 bv->owner()->allowInput();
529 void MenuLayoutSave(BufferView * bv)
531 if (!bv->available())
534 if (AskQuestion(_("Do you want to save the current settings"),
535 _("for Character, Document, Paper and Quotes"),
536 _("as default for new documents?")))
537 bv->buffer()->saveParamsAsDefaults();
541 // This function runs "configure" and then rereads lyx.defaults to
542 // reconfigure the automatic settings.
543 void Reconfigure(BufferView * bv)
545 bv->owner()->message(_("Running configure..."));
547 // Run configure in user lyx directory
549 Systemcalls one(Systemcalls::System,
550 AddName(system_lyxdir, "configure"));
552 bv->owner()->message(_("Reloading configuration..."));
553 lyxrc.read(LibFileSearch(string(), "lyxrc.defaults"));
554 WriteAlert(_("The system has been reconfigured."),
555 _("You need to restart LyX to make use of any"),
556 _("updated document class specifications."));