1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich,
7 * Copyright 1995-2000 The LyX Team.
9 * ====================================================== */
18 #include FORMS_H_LOCATION
22 #include "insets/insetlabel.h"
23 #include "insets/figinset.h"
24 #include "minibuffer.h"
25 #include "bufferlist.h"
26 #include "frontends/FileDialog.h"
27 #include "lyx_gui_misc.h"
29 #include "lastfiles.h"
30 #include "bufferview_funcs.h"
31 #include "support/FileInfo.h"
32 #include "support/syscall.h"
33 #include "support/filetools.h"
34 #include "support/path.h"
42 using std::back_inserter;
43 using std::istream_iterator;
47 extern BufferList bufferlist;
48 extern FD_form_figure * fd_form_figure;
50 extern BufferView * current_view; // called too many times in this file...
52 // this should be static, but I need it in buffer.C
53 bool quitting; // flag, that we are quitting the program
54 extern bool finished; // all cleanup done just let it run through now.
57 This is the inset locking stuff needed for mathed --------------------
59 an inset can simple call LockInset in it's edit call and *ONLY* in it's
61 Inset::Edit() can only be called by the main lyx module.
63 Then the inset may modify the menu's and/or iconbars.
65 Unlocking is either done by LyX or the inset itself with a UnlockInset-call
67 During the lock, all button and keyboard events will be modified
68 and send to the inset through the following inset-features. Note that
69 Inset::InsetUnlock will be called from inside UnlockInset. It is meant
70 to contain the code for restoring the menus and things like this.
73 virtual void InsetButtonPress(int x, int y, int button);
74 virtual void InsetButtonRelease(int x, int y, int button);
75 virtual void InsetKeyPress(XKeyEvent *ev);
76 virtual void InsetMotionNotify(int x, int y, int state);
77 virtual void InsetUnlock();
79 If a inset wishes any redraw and/or update it just has to call
81 It's is completly irrelevant, where the inset is. UpdateInset will
82 find it in any paragraph in any buffer.
83 Of course the_locking_inset and the insets in the current paragraph/buffer
84 are checked first, so no performance problem should occur.
86 Hope that's ok for the beginning, Alejandro,
87 sorry that I needed so much time,
92 //void UpdateInset(BufferView * bv, Inset * inset, bool mark_dirty = true);
94 /* these functions return 1 if an error occured,
96 // Now they work only for updatable insets. [Alejandro 080596]
97 //int LockInset(UpdatableInset * inset);
98 void ToggleLockedInsetCursor(int x, int y, int asc, int desc);
99 //void FitLockedInsetCursor(long x, long y, int asc, int desc);
100 //int UnlockInset(UpdatableInset * inset);
101 //void LockedInsetStoreUndo(Undo::undo_kind kind);
103 /* this is for asyncron updating. UpdateInsetUpdateList will be called
104 automatically from LyX. Just insert the Inset into the Updatelist */
105 //void UpdateInsetUpdateList();
106 //void PutInsetIntoInsetUpdateList(Inset * inset);
108 //InsetUpdateStruct * InsetUpdateList = 0;
112 -----------------------------------------------------------------------
116 void ShowMessage(Buffer const * buf,
119 string const & msg3, int delay)
122 buf->getUser()->owner()->getMiniBuffer()->Set(msg1, msg2,
125 lyxerr << msg1 << msg2 << msg3 << endl;
136 // should be moved to lyxfunc.C
137 bool MenuWrite(BufferView * bv, Buffer * buffer)
140 XFlush(fl_get_display());
142 if (!buffer->save()) {
143 string const fname = buffer->fileName();
144 string const s = MakeAbsPath(fname);
145 if (AskQuestion(_("Save failed. Rename and try again?"),
146 MakeDisplayPath(s, 50),
147 _("(If not, document is not saved.)"))) {
148 return WriteAs(bv, buffer);
152 lastfiles->newFile(buffer->fileName());
158 // should be moved to BufferView.C
159 // Half of this func should be in LyXView, the rest in BufferView.
160 bool WriteAs(BufferView * bv, Buffer * buffer, string const & filename)
162 string fname = buffer->fileName();
163 string oldname = fname;
165 if (filename.empty()) {
167 FileDialog fileDlg(bv->owner(),
168 _("Choose a filename to save document as"),
170 make_pair(string(_("Documents")),
171 string(lyxrc.document_path)),
172 make_pair(string(_("Templates")),
173 string(lyxrc.template_path)));
175 if (!IsLyXFilename(fname))
178 FileDialog::Result result =
179 fileDlg.Select(OnlyPath(fname),
180 _("*.lyx|LyX Documents (*.lyx)"),
181 OnlyFilename(fname));
183 if (result.first == FileDialog::Later)
186 fname = result.second;
191 // Make sure the absolute filename ends with appropriate suffix
192 fname = MakeAbsPath(fname);
193 if (!IsLyXFilename(fname))
198 // Same name as we have already?
199 if (!buffer->isUnnamed() && fname == oldname) {
200 if (!AskQuestion(_("Same name as document already has:"),
201 MakeDisplayPath(fname, 50),
204 // Falls through to name change and save
206 // No, but do we have another file with this name open?
207 else if (!buffer->isUnnamed() && bufferlist.exists(fname)) {
208 if (AskQuestion(_("Another document with same name open!"),
209 MakeDisplayPath(fname, 50),
210 _("Replace with current document?")))
212 bufferlist.close(bufferlist.getBuffer(fname));
214 // Ok, change the name of the buffer, but don't save!
215 buffer->setFileName(fname);
218 ShowMessage(buffer, _("Document renamed to '"),
219 MakeDisplayPath(fname), _("', but not saved..."));
222 } // Check whether the file exists
224 FileInfo const myfile(fname);
225 if (myfile.isOK() && !AskQuestion(_("Document already exists:"),
226 MakeDisplayPath(fname, 50),
231 // Ok, change the name of the buffer
232 buffer->setFileName(fname);
234 bool unnamed = buffer->isUnnamed();
235 buffer->setUnnamed(false);
237 if (!MenuWrite(bv, buffer)) {
238 buffer->setFileName(oldname);
239 buffer->setUnnamed(unnamed);
240 ShowMessage(buffer, _("Document could not be saved!"),
241 _("Holding the old name."), MakeDisplayPath(oldname));
244 // now remove the oldname autosave file if existant!
245 removeAutosaveFile(oldname);
250 int MenuRunChktex(Buffer * buffer)
254 if (buffer->isSGML()) {
255 WriteAlert(_("Chktex does not work with SGML derived documents."));
258 ret = buffer->runChktex();
264 s = _("No warnings found.");
265 } else if (ret == 1) {
266 s = _("One warning found.");
267 t = _("Use 'Edit->Go to Error' to find it.");
270 s += _(" warnings found.");
271 t = _("Use 'Edit->Go to Error' to find them.");
273 WriteAlert(_("Chktex run successfully"), s, t);
275 WriteAlert(_("Error!"), _("It seems chktex does not work."));
283 lyxerr.debug() << "Running QuitLyX." << endl;
286 if (!bufferlist.QwriteAll())
289 lastfiles->writeFile(lyxrc.lastfiles);
292 // Set a flag that we do quitting from the program,
293 // so no refreshes are necessary.
296 // close buffers first
297 bufferlist.closeAll();
299 // do any other cleanup procedures now
300 lyxerr.debug() << "Deleting tmp dir " << system_tempdir << endl;
302 DestroyLyXTmpDir(system_tempdir);
309 void AutoSave(BufferView * bv)
310 // should probably be moved into BufferList (Lgb)
311 // Perfect target for a thread...
313 if (!bv->available())
316 if (bv->buffer()->isBakClean() || bv->buffer()->isReadonly()) {
317 // We don't save now, but we'll try again later
318 bv->owner()->resetAutosaveTimer();
322 bv->owner()->getMiniBuffer()->Set(_("Autosaving current document..."));
324 // create autosave filename
325 string fname = OnlyPath(bv->buffer()->fileName());
327 fname += OnlyFilename(bv->buffer()->fileName());
330 // tmp_ret will be located (usually) in /tmp
331 // will that be a problem?
332 pid_t const pid = fork(); // If you want to debug the autosave
333 // you should set pid to -1, and comment out the
335 if (pid == 0 || pid == -1) {
336 // pid = -1 signifies that lyx was unable
337 // to fork. But we will do the save
341 string const tmp_ret = lyx::tempName(string(), "lyxauto");
342 if (!tmp_ret.empty()) {
343 bv->buffer()->writeFile(tmp_ret, 1);
344 // assume successful write of tmp_ret
345 if (!lyx::rename(tmp_ret, fname)) {
347 // most likely couldn't move between filesystems
348 // unless write of tmp_ret failed
349 // so remove tmp file (if it exists)
350 lyx::unlink(tmp_ret);
357 // failed to write/rename tmp_ret so try writing direct
358 if (!bv->buffer()->writeFile(fname, 1)) {
359 // It is dangerous to do this in the child,
360 // but safe in the parent, so...
362 bv->owner()->getMiniBuffer()->Set(_("Autosave Failed!"));
365 if (pid == 0) { // we are the child so...
370 bv->buffer()->markBakClean();
371 bv->owner()->resetAutosaveTimer();
376 // Copyright CHT Software Service GmbH
379 // create new file with template
382 Buffer * NewLyxFile(string const & filename)
384 // Split argument by :
386 string tmpname = split(filename, name, ':');
387 #ifdef __EMX__ // Fix me! lyx_cb.C may not be low level enough to allow this.
388 if (name.length() == 1
389 && isalpha(static_cast<unsigned char>(name[0]))
390 && (prefixIs(tmpname, "/") || prefixIs(tmpname, "\\"))) {
392 name += token(tmpname, ':', 0);
393 tmpname = split(tmpname, ':');
396 lyxerr.debug() << "Arg is " << filename
397 << "\nName is " << name
398 << "\nTemplate is " << tmpname << endl;
400 // find a free buffer
401 Buffer * tmpbuf = bufferlist.newFile(name, tmpname);
403 lastfiles->newFile(tmpbuf->fileName());
408 // Insert ascii file (if filename is empty, prompt for one)
409 void InsertAsciiFile(BufferView * bv, string const & f, bool asParagraph)
413 if (!bv->available())
417 FileDialog fileDlg(bv->owner(), _("Select file to insert"),
418 (asParagraph) ? LFUN_FILE_INSERT_ASCII_PARA : LFUN_FILE_INSERT_ASCII);
420 FileDialog::Result result = fileDlg.Select(bv->owner()->buffer()->filepath);
422 if (result.first == FileDialog::Later)
425 fname = result.second;
433 if (!fi.readable()) {
434 WriteFSAlert(_("Error! Specified file is unreadable: "),
435 MakeDisplayPath(fname, 50));
439 ifstream ifs(fname.c_str());
441 WriteFSAlert(_("Error! Cannot open specified file: "),
442 MakeDisplayPath(fname, 50));
446 ifs.unsetf(ios::skipws);
447 istream_iterator<char> ii(ifs);
448 istream_iterator<char> end;
449 #if !defined(USE_INCLUDED_STRING) && !defined(STD_STRING_IS_GOOD)
450 // We use this until the compilers get better...
452 copy(ii, end, back_inserter(tmp));
453 string const tmpstr(tmp.begin(), tmp.end());
455 // This is what we want to use and what we will use once the
456 // compilers get good enough.
457 //string tmpstr(ii, end); // yet a reason for using std::string
458 // alternate approach to get the file into a string:
460 copy(ii, end, back_inserter(tmpstr));
465 // clear the selection
466 bv->beforeChange(bv->text);
468 bv->text->InsertStringA(bv, tmpstr);
470 bv->text->InsertStringB(bv, tmpstr);
471 bv->update(bv->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
475 void MenuInsertLabel(BufferView * bv, string const & arg)
482 bv->text->cursor.par()->FirstPhysicalPar();
484 LyXParagraph * par = bv->text->cursor.par();
486 LyXLayout const * layout =
487 &textclasslist.Style(bv->buffer()->params.textclass,
491 if (layout->latextype == LATEX_PARAGRAPH && par->previous_) {
492 LyXParagraph * par2 = par->previous_->FirstPhysicalPar();
494 if (layout->latextype == LATEX_PARAGRAPH && par->previous()) {
495 LyXParagraph * par2 = par->previous();
497 LyXLayout const * layout2 =
498 &textclasslist.Style(bv->buffer()->params.textclass,
500 if (layout2->latextype != LATEX_PARAGRAPH) {
505 string text = layout->latexname().substr(0, 3);
506 if (layout->latexname() == "theorem")
507 text = "thm"; // Create a correct prefix for prettyref
509 if (par->footnoteflag==LyXParagraph::OPEN_FOOTNOTE)
510 switch (par->footnotekind) {
511 case LyXParagraph::FIG:
512 case LyXParagraph::WIDE_FIG:
515 case LyXParagraph::TAB:
516 case LyXParagraph::WIDE_TAB:
519 case LyXParagraph::ALGORITHM:
522 case LyXParagraph::FOOTNOTE:
523 case LyXParagraph::MARGIN:
528 if (layout->latextype == LATEX_PARAGRAPH ||
529 lyxrc.label_init_length < 0)
531 string par_text = par->String(bv->buffer(), false);
532 for (int i = 0; i < lyxrc.label_init_length; ++i) {
533 if (par_text.empty())
536 par_text = split(par_text, head, ' ');
538 text += '-'; // Is it legal to use spaces in
543 pair<bool, string> result =
544 askForText(_("Enter new label to insert:"), text);
546 label = frontStrip(strip(result.second));
549 if (!label.empty()) {
550 InsetCommandParams p( "label", label );
551 InsetLabel * inset = new InsetLabel( p );
552 bv->insertInset( inset );
558 void MenuLayoutSave(BufferView * bv)
560 if (!bv->available())
563 if (AskQuestion(_("Do you want to save the current settings"),
564 _("for Character, Document, Paper and Quotes"),
565 _("as default for new documents?")))
566 bv->buffer()->saveParamsAsDefaults();
572 if (fd_form_figure->form_figure->visible) {
573 fl_raise_form(fd_form_figure->form_figure);
575 fl_show_form(fd_form_figure->form_figure,
576 FL_PLACE_MOUSE | FL_FREE_SIZE, FL_TRANSIENT,
582 /* callbacks for form form_figure */
584 void FigureApplyCB(FL_OBJECT *, long)
586 if (!current_view->available())
589 Buffer * buffer = current_view->buffer();
590 if (buffer->isReadonly()) // paranoia
593 current_view->owner()->getMiniBuffer()->Set(_("Inserting figure..."));
594 if (fl_get_button(fd_form_figure->radio_inline)) {
595 InsetFig * new_inset = new InsetFig(100, 20, *buffer);
596 current_view->insertInset(new_inset);
597 current_view->owner()->getMiniBuffer()->Set(_("Figure inserted"));
598 new_inset->Edit(current_view, 0, 0, 0);
602 current_view->hideCursor();
603 current_view->update(current_view->text, BufferView::SELECT|BufferView::FITCUR);
604 current_view->beforeChange(current_view->text);
606 current_view->text->SetCursorParUndo(current_view->buffer());
607 current_view->text->FreezeUndo();
609 current_view->text->BreakParagraph(current_view);
610 current_view->update(current_view->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
613 if (current_view->text->cursor.par()->Last()) {
615 if (current_view->text->cursor.par()->size()) {
617 current_view->text->CursorLeft(current_view);
619 current_view->text->BreakParagraph(current_view);
620 current_view->update(current_view->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
623 // The standard layout should always be numer 0;
624 current_view->text->SetLayout(current_view, 0);
627 if (current_view->text->cursor.par()->footnoteflag ==
628 LyXParagraph::NO_FOOTNOTE) {
631 SetParagraph(current_view, 0, 0,
633 VSpace (0.3 * buffer->params.spacing.getValue(),
636 buffer->params.spacing.getValue(),
638 LYX_ALIGN_CENTER, string(), 0);
641 current_view->text->SetParagraph(current_view, 0, 0,
643 VSpace(VSpace::NONE),
644 VSpace(VSpace::NONE),
651 current_view->update(current_view->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
653 Inset * new_inset = new InsetFig(100, 100, *buffer);
654 current_view->insertInset(new_inset);
655 new_inset->Edit(current_view, 0, 0, 0);
656 current_view->update(current_view->text, BufferView::SELECT|BufferView::FITCUR);
657 current_view->owner()->getMiniBuffer()->Set(_("Figure inserted"));
658 current_view->text->UnFreezeUndo();
659 current_view->setState();
664 void FigureCancelCB(FL_OBJECT *, long)
666 fl_hide_form(fd_form_figure->form_figure);
671 void FigureOKCB(FL_OBJECT * ob, long data)
673 FigureApplyCB(ob, data);
674 FigureCancelCB(ob, data);
678 // This function runs "configure" and then rereads lyx.defaults to
679 // reconfigure the automatic settings.
680 void Reconfigure(BufferView * bv)
682 bv->owner()->getMiniBuffer()->Set(_("Running configure..."));
684 // Run configure in user lyx directory
686 Systemcalls one(Systemcalls::System,
687 AddName(system_lyxdir, "configure"));
689 bv->owner()->getMiniBuffer()->Set(_("Reloading configuration..."));
690 lyxrc.read(LibFileSearch(string(), "lyxrc.defaults"));
691 WriteAlert(_("The system has been reconfigured."),
692 _("You need to restart LyX to make use of any"),
693 _("updated document class specifications."));