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"
25 #include "minibuffer.h"
27 #include "bufferlist.h"
28 #include "frontends/FileDialog.h"
29 #include "lyx_gui_misc.h"
31 #include "lastfiles.h"
32 #include "bufferview_funcs.h"
33 #include "support/FileInfo.h"
34 #include "support/syscall.h"
35 #include "support/filetools.h"
36 #include "support/path.h"
42 using std::back_inserter;
46 using std::istream_iterator;
53 extern BufferList bufferlist;
54 extern void show_symbols_form();
55 extern FD_form_figure * fd_form_figure;
57 extern BufferView * current_view; // called too many times in this file...
59 extern void DeleteSimpleCutBuffer(); /* for the cleanup when exiting */
61 extern void MenuSendto();
63 // this should be static, but I need it in buffer.C
64 bool quitting; // flag, that we are quitting the program
65 extern bool finished; // all cleanup done just let it run through now.
67 char ascii_type; /* for selection notify callbacks */
69 bool scrolling = false;
72 This is the inset locking stuff needed for mathed --------------------
74 an inset can simple call LockInset in it's edit call and *ONLY* in it's
76 Inset::Edit() can only be called by the main lyx module.
78 Then the inset may modify the menu's and/or iconbars.
80 Unlocking is either done by LyX or the inset itself with a UnlockInset-call
82 During the lock, all button and keyboard events will be modified
83 and send to the inset through the following inset-features. Note that
84 Inset::InsetUnlock will be called from inside UnlockInset. It is meant
85 to contain the code for restoring the menus and things like this.
88 virtual void InsetButtonPress(int x, int y, int button);
89 virtual void InsetButtonRelease(int x, int y, int button);
90 virtual void InsetKeyPress(XKeyEvent *ev);
91 virtual void InsetMotionNotify(int x, int y, int state);
92 virtual void InsetUnlock();
94 If a inset wishes any redraw and/or update it just has to call
96 It's is completly irrelevant, where the inset is. UpdateInset will
97 find it in any paragraph in any buffer.
98 Of course the_locking_inset and the insets in the current paragraph/buffer
99 are checked first, so no performance problem should occur.
101 Hope that's ok for the beginning, Alejandro,
102 sorry that I needed so much time,
107 //void UpdateInset(BufferView * bv, Inset * inset, bool mark_dirty = true);
109 /* these functions return 1 if an error occured,
111 // Now they work only for updatable insets. [Alejandro 080596]
112 //int LockInset(UpdatableInset * inset);
113 void ToggleLockedInsetCursor(int x, int y, int asc, int desc);
114 //void FitLockedInsetCursor(long x, long y, int asc, int desc);
115 //int UnlockInset(UpdatableInset * inset);
116 //void LockedInsetStoreUndo(Undo::undo_kind kind);
118 /* this is for asyncron updating. UpdateInsetUpdateList will be called
119 automatically from LyX. Just insert the Inset into the Updatelist */
120 //void UpdateInsetUpdateList();
121 //void PutInsetIntoInsetUpdateList(Inset * inset);
123 //InsetUpdateStruct * InsetUpdateList = 0;
127 -----------------------------------------------------------------------
131 void ShowMessage(Buffer const * buf,
134 string const & msg3, int delay)
137 buf->getUser()->owner()->getMiniBuffer()->Set(msg1, msg2,
140 lyxerr << msg1 << msg2 << msg3 << endl;
151 // should be moved to lyxfunc.C
152 bool MenuWrite(BufferView * bv, Buffer * buffer)
155 XFlush(fl_get_display());
157 if (!buffer->save()) {
158 string const fname = buffer->fileName();
159 string const s = MakeAbsPath(fname);
160 if (AskQuestion(_("Save failed. Rename and try again?"),
161 MakeDisplayPath(s, 50),
162 _("(If not, document is not saved.)"))) {
163 return WriteAs(bv, buffer);
167 lastfiles->newFile(buffer->fileName());
173 // should be moved to BufferView.C
174 // Half of this func should be in LyXView, the rest in BufferView.
175 bool WriteAs(BufferView * bv, Buffer * buffer, string const & filename)
177 string fname = buffer->fileName();
178 string oldname = fname;
180 if (filename.empty()) {
182 FileDialog fileDlg(bv->owner(), _("Choose a filename to save document as"),
184 make_pair(string(_("Documents")), string(lyxrc.document_path)),
185 make_pair(string(_("Templates")), string(lyxrc.template_path)));
187 if (!IsLyXFilename(fname))
190 FileDialog::Result result = fileDlg.Select(OnlyPath(fname), _("*.lyx|LyX Documents (*.lyx)"), OnlyFilename(fname));
192 if (result.first == FileDialog::Later)
195 fname = result.second;
200 // Make sure the absolute filename ends with appropriate suffix
201 fname = MakeAbsPath(fname);
202 if (!IsLyXFilename(fname))
208 // Same name as we have already?
209 if (!buffer->isUnnamed() && fname == oldname) {
210 if (!AskQuestion(_("Same name as document already has:"),
211 MakeDisplayPath(fname, 50),
214 // Falls through to name change and save
216 // No, but do we have another file with this name open?
217 else if (!buffer->isUnnamed() && bufferlist.exists(fname)) {
218 if (AskQuestion(_("Another document with same name open!"),
219 MakeDisplayPath(fname, 50),
220 _("Replace with current document?")))
222 bufferlist.close(bufferlist.getBuffer(fname));
224 // Ok, change the name of the buffer, but don't save!
225 buffer->setFileName(fname);
228 ShowMessage(buffer, _("Document renamed to '"),
229 MakeDisplayPath(fname), _("', but not saved..."));
232 } // Check whether the file exists
234 FileInfo const myfile(fname);
235 if (myfile.isOK() && !AskQuestion(_("Document already exists:"),
236 MakeDisplayPath(fname, 50),
241 // Ok, change the name of the buffer
242 buffer->setFileName(fname);
244 bool unnamed = buffer->isUnnamed();
245 buffer->setUnnamed(false);
247 if (!MenuWrite(bv, buffer)) {
248 buffer->setFileName(oldname);
249 buffer->setUnnamed(unnamed);
250 ShowMessage(buffer, _("Document could not be saved!"),
251 _("Holding the old name."), MakeDisplayPath(oldname));
254 // now remove the oldname autosave file if existant!
255 removeAutosaveFile(oldname);
260 int MenuRunChktex(Buffer * buffer)
264 if (buffer->isSGML()) {
265 WriteAlert(_("Chktex does not work with SGML derived documents."));
268 ret = buffer->runChktex();
274 s = _("No warnings found.");
275 } else if (ret == 1) {
276 s = _("One warning found.");
277 t = _("Use 'Edit->Go to Error' to find it.");
280 s += _(" warnings found.");
281 t = _("Use 'Edit->Go to Error' to find them.");
283 WriteAlert(_("Chktex run successfully"), s, t);
285 WriteAlert(_("Error!"), _("It seems chktex does not work."));
293 lyxerr.debug() << "Running QuitLyX." << endl;
296 if (!bufferlist.QwriteAll())
299 lastfiles->writeFile(lyxrc.lastfiles);
302 // Set a flag that we do quitting from the program,
303 // so no refreshes are necessary.
306 // close buffers first
307 bufferlist.closeAll();
309 // do any other cleanup procedures now
310 lyxerr.debug() << "Deleting tmp dir " << system_tempdir << endl;
312 DestroyLyXTmpDir(system_tempdir);
319 void AutoSave(BufferView * bv)
320 // should probably be moved into BufferList (Lgb)
321 // Perfect target for a thread...
323 if (!bv->available())
326 if (bv->buffer()->isBakClean() || bv->buffer()->isReadonly()) {
327 // We don't save now, but we'll try again later
328 bv->owner()->resetAutosaveTimer();
332 bv->owner()->getMiniBuffer()->Set(_("Autosaving current document..."));
334 // create autosave filename
335 string fname = OnlyPath(bv->buffer()->fileName());
337 fname += OnlyFilename(bv->buffer()->fileName());
340 // tmp_ret will be located (usually) in /tmp
341 // will that be a problem?
342 pid_t const pid = fork(); // If you want to debug the autosave
343 // you should set pid to -1, and comment out the
345 if (pid == 0 || pid == -1) {
346 // pid = -1 signifies that lyx was unable
347 // to fork. But we will do the save
351 string const tmp_ret = lyx::tempName(string(), "lyxauto");
352 if (!tmp_ret.empty()) {
353 bv->buffer()->writeFile(tmp_ret, 1);
354 // assume successful write of tmp_ret
355 if (!lyx::rename(tmp_ret, fname)) {
357 // most likely couldn't move between filesystems
358 // unless write of tmp_ret failed
359 // so remove tmp file (if it exists)
360 lyx::unlink(tmp_ret);
367 // failed to write/rename tmp_ret so try writing direct
368 if (!bv->buffer()->writeFile(fname, 1)) {
369 // It is dangerous to do this in the child,
370 // but safe in the parent, so...
372 bv->owner()->getMiniBuffer()->Set(_("Autosave Failed!"));
375 if (pid == 0) { // we are the child so...
380 bv->buffer()->markBakClean();
381 bv->owner()->resetAutosaveTimer();
386 // Copyright CHT Software Service GmbH
389 // create new file with template
392 Buffer * NewLyxFile(string const & filename)
394 // Split argument by :
396 string tmpname = split(filename, name, ':');
397 #ifdef __EMX__ // Fix me! lyx_cb.C may not be low level enough to allow this.
398 if (name.length() == 1
399 && isalpha(static_cast<unsigned char>(name[0]))
400 && (prefixIs(tmpname, "/") || prefixIs(tmpname, "\\"))) {
402 name += token(tmpname, ':', 0);
403 tmpname = split(tmpname, ':');
406 lyxerr.debug() << "Arg is " << filename
407 << "\nName is " << name
408 << "\nTemplate is " << tmpname << endl;
410 // find a free buffer
411 Buffer * tmpbuf = bufferlist.newFile(name, tmpname);
413 lastfiles->newFile(tmpbuf->fileName());
418 // Insert ascii file (if filename is empty, prompt for one)
419 void InsertAsciiFile(BufferView * bv, string const & f, bool asParagraph)
423 if (!bv->available())
427 FileDialog fileDlg(bv->owner(), _("Select file to insert"),
428 (asParagraph) ? LFUN_FILE_INSERT_ASCII_PARA : LFUN_FILE_INSERT_ASCII);
430 FileDialog::Result result = fileDlg.Select(bv->owner()->buffer()->filepath);
432 if (result.first == FileDialog::Later)
435 fname = result.second;
443 if (!fi.readable()) {
444 WriteFSAlert(_("Error! Specified file is unreadable: "),
445 MakeDisplayPath(fname, 50));
449 ifstream ifs(fname.c_str());
451 WriteFSAlert(_("Error! Cannot open specified file: "),
452 MakeDisplayPath(fname, 50));
456 ifs.unsetf(ios::skipws);
457 istream_iterator<char> ii(ifs);
458 istream_iterator<char> end;
459 #if !defined(USE_INCLUDED_STRING) && !defined(STD_STRING_IS_GOOD)
460 // We use this until the compilers get better...
462 copy(ii, end, back_inserter(tmp));
463 string const tmpstr(tmp.begin(), tmp.end());
465 // This is what we want to use and what we will use once the
466 // compilers get good enough.
467 //string tmpstr(ii, end); // yet a reason for using std::string
468 // alternate approach to get the file into a string:
470 copy(ii, end, back_inserter(tmpstr));
475 // clear the selection
476 bv->beforeChange(bv->text);
478 bv->text->InsertStringA(bv, tmpstr);
480 bv->text->InsertStringB(bv, tmpstr);
481 bv->update(bv->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
485 void MenuInsertLabel(BufferView * bv, string const & arg)
492 bv->text->cursor.par()->FirstPhysicalPar();
494 LyXParagraph * par = bv->text->cursor.par();
496 LyXLayout const * layout =
497 &textclasslist.Style(bv->buffer()->params.textclass,
501 if (layout->latextype == LATEX_PARAGRAPH && par->previous_) {
502 LyXParagraph * par2 = par->previous_->FirstPhysicalPar();
504 if (layout->latextype == LATEX_PARAGRAPH && par->previous()) {
505 LyXParagraph * par2 = par->previous();
507 LyXLayout const * layout2 =
508 &textclasslist.Style(bv->buffer()->params.textclass,
510 if (layout2->latextype != LATEX_PARAGRAPH) {
515 string text = layout->latexname().substr(0, 3);
516 if (layout->latexname() == "theorem")
517 text = "thm"; // Create a correct prefix for prettyref
519 if (par->footnoteflag==LyXParagraph::OPEN_FOOTNOTE)
520 switch (par->footnotekind) {
521 case LyXParagraph::FIG:
522 case LyXParagraph::WIDE_FIG:
525 case LyXParagraph::TAB:
526 case LyXParagraph::WIDE_TAB:
529 case LyXParagraph::ALGORITHM:
532 case LyXParagraph::FOOTNOTE:
533 case LyXParagraph::MARGIN:
538 if (layout->latextype == LATEX_PARAGRAPH ||
539 lyxrc.label_init_length < 0)
541 string par_text = par->String(bv->buffer(), false);
542 for (int i = 0; i < lyxrc.label_init_length; ++i) {
543 if (par_text.empty())
546 par_text = split(par_text, head, ' ');
548 text += '-'; // Is it legal to use spaces in
553 pair<bool, string> result =
554 askForText(_("Enter new label to insert:"), text);
556 label = frontStrip(strip(result.second));
559 if (!label.empty()) {
560 InsetCommandParams p( "label", label );
561 InsetLabel * inset = new InsetLabel( p );
562 bv->insertInset( inset );
568 void MenuLayoutSave(BufferView * bv)
570 if (!bv->available())
573 if (AskQuestion(_("Do you want to save the current settings"),
574 _("for Character, Document, Paper and Quotes"),
575 _("as default for new documents?")))
576 bv->buffer()->saveParamsAsDefaults();
582 if (fd_form_figure->form_figure->visible) {
583 fl_raise_form(fd_form_figure->form_figure);
585 fl_show_form(fd_form_figure->form_figure,
586 FL_PLACE_MOUSE | FL_FREE_SIZE, FL_TRANSIENT,
592 /* callbacks for form form_figure */
594 void FigureApplyCB(FL_OBJECT *, long)
596 if (!current_view->available())
599 Buffer * buffer = current_view->buffer();
600 if (buffer->isReadonly()) // paranoia
603 current_view->owner()->getMiniBuffer()->Set(_("Inserting figure..."));
604 if (fl_get_button(fd_form_figure->radio_inline)) {
605 InsetFig * new_inset = new InsetFig(100, 20, *buffer);
606 current_view->insertInset(new_inset);
607 current_view->owner()->getMiniBuffer()->Set(_("Figure inserted"));
608 new_inset->Edit(current_view, 0, 0, 0);
612 current_view->hideCursor();
613 current_view->update(current_view->text, BufferView::SELECT|BufferView::FITCUR);
614 current_view->beforeChange(current_view->text);
616 current_view->text->SetCursorParUndo(current_view->buffer());
617 current_view->text->FreezeUndo();
619 current_view->text->BreakParagraph(current_view);
620 current_view->update(current_view->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
623 if (current_view->text->cursor.par()->Last()) {
625 if (current_view->text->cursor.par()->size()) {
627 current_view->text->CursorLeft(current_view);
629 current_view->text->BreakParagraph(current_view);
630 current_view->update(current_view->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
633 // The standard layout should always be numer 0;
634 current_view->text->SetLayout(current_view, 0);
637 if (current_view->text->cursor.par()->footnoteflag ==
638 LyXParagraph::NO_FOOTNOTE) {
641 SetParagraph(current_view, 0, 0,
643 VSpace (0.3 * buffer->params.spacing.getValue(),
646 buffer->params.spacing.getValue(),
648 LYX_ALIGN_CENTER, string(), 0);
651 current_view->text->SetParagraph(current_view, 0, 0,
653 VSpace(VSpace::NONE),
654 VSpace(VSpace::NONE),
661 current_view->update(current_view->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
663 Inset * new_inset = new InsetFig(100, 100, *buffer);
664 current_view->insertInset(new_inset);
665 new_inset->Edit(current_view, 0, 0, 0);
666 current_view->update(current_view->text, BufferView::SELECT|BufferView::FITCUR);
667 current_view->owner()->getMiniBuffer()->Set(_("Figure inserted"));
668 current_view->text->UnFreezeUndo();
669 current_view->setState();
673 extern "C" void FigureCancelCB(FL_OBJECT *, long)
675 fl_hide_form(fd_form_figure->form_figure);
679 extern "C" void FigureOKCB(FL_OBJECT * ob, long data)
681 FigureApplyCB(ob, data);
682 FigureCancelCB(ob, data);
686 // This function runs "configure" and then rereads lyx.defaults to
687 // reconfigure the automatic settings.
688 void Reconfigure(BufferView * bv)
690 bv->owner()->getMiniBuffer()->Set(_("Running configure..."));
692 // Run configure in user lyx directory
694 Systemcalls one(Systemcalls::System,
695 AddName(system_lyxdir, "configure"));
697 bv->owner()->getMiniBuffer()->Set(_("Reloading configuration..."));
698 lyxrc.read(LibFileSearch(string(), "lyxrc.defaults"));
699 WriteAlert(_("The system has been reconfigured."),
700 _("You need to restart LyX to make use of any"),
701 _("updated document class specifications."));