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 * ====================================================== */
17 #include FORMS_H_LOCATION
21 #include "insets/insetlabel.h"
22 #include "insets/figinset.h"
24 #include "minibuffer.h"
26 #include "bufferlist.h"
28 #include "lyx_gui_misc.h"
30 #include "lastfiles.h"
31 #include "bufferview_funcs.h"
32 #include "support/FileInfo.h"
33 #include "support/syscall.h"
34 #include "support/filetools.h"
35 #include "support/path.h"
41 using std::back_inserter;
45 using std::istream_iterator;
51 extern BufferList bufferlist;
52 extern void show_symbols_form();
53 extern FD_form_figure * fd_form_figure;
55 extern BufferView * current_view; // called too many times in this file...
57 extern void DeleteSimpleCutBuffer(); /* for the cleanup when exiting */
59 extern void MenuSendto();
61 // this should be static, but I need it in buffer.C
62 bool quitting; // flag, that we are quitting the program
63 extern bool finished; // all cleanup done just let it run through now.
65 char ascii_type; /* for selection notify callbacks */
67 bool scrolling = false;
70 This is the inset locking stuff needed for mathed --------------------
72 an inset can simple call LockInset in it's edit call and *ONLY* in it's
74 Inset::Edit() can only be called by the main lyx module.
76 Then the inset may modify the menu's and/or iconbars.
78 Unlocking is either done by LyX or the inset itself with a UnlockInset-call
80 During the lock, all button and keyboard events will be modified
81 and send to the inset through the following inset-features. Note that
82 Inset::InsetUnlock will be called from inside UnlockInset. It is meant
83 to contain the code for restoring the menus and things like this.
86 virtual void InsetButtonPress(int x, int y, int button);
87 virtual void InsetButtonRelease(int x, int y, int button);
88 virtual void InsetKeyPress(XKeyEvent *ev);
89 virtual void InsetMotionNotify(int x, int y, int state);
90 virtual void InsetUnlock();
92 If a inset wishes any redraw and/or update it just has to call
94 It's is completly irrelevant, where the inset is. UpdateInset will
95 find it in any paragraph in any buffer.
96 Of course the_locking_inset and the insets in the current paragraph/buffer
97 are checked first, so no performance problem should occur.
99 Hope that's ok for the beginning, Alejandro,
100 sorry that I needed so much time,
105 //void UpdateInset(BufferView * bv, Inset * inset, bool mark_dirty = true);
107 /* these functions return 1 if an error occured,
109 // Now they work only for updatable insets. [Alejandro 080596]
110 //int LockInset(UpdatableInset * inset);
111 void ToggleLockedInsetCursor(int x, int y, int asc, int desc);
112 //void FitLockedInsetCursor(long x, long y, int asc, int desc);
113 //int UnlockInset(UpdatableInset * inset);
114 //void LockedInsetStoreUndo(Undo::undo_kind kind);
116 /* this is for asyncron updating. UpdateInsetUpdateList will be called
117 automatically from LyX. Just insert the Inset into the Updatelist */
118 //void UpdateInsetUpdateList();
119 //void PutInsetIntoInsetUpdateList(Inset * inset);
121 //InsetUpdateStruct * InsetUpdateList = 0;
125 -----------------------------------------------------------------------
129 void ShowMessage(Buffer const * buf,
132 string const & msg3, int delay)
135 buf->getUser()->owner()->getMiniBuffer()->Set(msg1, msg2,
138 lyxerr << msg1 << msg2 << msg3 << endl;
150 // should be moved to lyxfunc.C
151 bool MenuWrite(BufferView * bv, Buffer * buffer)
153 XFlush(fl_get_display());
154 if (!buffer->save()) {
155 string const fname = buffer->fileName();
156 string const s = MakeAbsPath(fname);
157 if (AskQuestion(_("Save failed. Rename and try again?"),
158 MakeDisplayPath(s, 50),
159 _("(If not, document is not saved.)"))) {
160 return MenuWriteAs(bv, buffer);
164 lastfiles->newFile(buffer->fileName());
170 // should be moved to BufferView.C
171 // Half of this func should be in LyXView, the rest in BufferView.
172 bool MenuWriteAs(BufferView * bv, Buffer * buffer)
174 // Why do we require BufferView::text to be able to write a
175 // document? I see no point in that. (Lgb)
176 //if (!bv->text) return;
178 string fname = buffer->fileName();
179 string oldname = fname;
183 fileDlg.SetButton(0, _("Documents"), lyxrc.document_path);
184 fileDlg.SetButton(1, _("Templates"), lyxrc.template_path);
186 if (!IsLyXFilename(fname))
189 fname = fileDlg.Select(_("Enter Filename to Save Document as"),
192 OnlyFilename(fname));
199 // Make sure the absolute filename ends with appropriate suffix
200 string s = MakeAbsPath(fname);
201 if (!IsLyXFilename(s))
204 // Same name as we have already?
205 if (!buffer->isUnnamed() && s == oldname) {
206 if (!AskQuestion(_("Same name as document already has:"),
207 MakeDisplayPath(s, 50),
210 // Falls through to name change and save
212 // No, but do we have another file with this name open?
213 else if (!buffer->isUnnamed() && bufferlist.exists(s)) {
214 if (AskQuestion(_("Another document with same name open!"),
215 MakeDisplayPath(s, 50),
216 _("Replace with current document?")))
218 bufferlist.close(bufferlist.getBuffer(s));
220 // Ok, change the name of the buffer, but don't save!
221 buffer->setFileName(s);
224 ShowMessage(buffer, _("Document renamed to '"),
225 MakeDisplayPath(s), _("', but not saved..."));
228 } // Check whether the file exists
230 FileInfo const myfile(s);
231 if (myfile.isOK() && !AskQuestion(_("Document already exists:"),
232 MakeDisplayPath(s, 50),
237 // Ok, change the name of the buffer
238 buffer->setFileName(s);
240 bool unnamed = buffer->isUnnamed();
241 buffer->setUnnamed(false);
243 // Small bug: If the save fails, we have irreversible changed the name
245 // Hope this is fixed this way! (Jug)
246 if (!MenuWrite(bv, buffer)) {
247 buffer->setFileName(oldname);
248 buffer->setUnnamed(unnamed);
249 ShowMessage(buffer, _("Document could not be saved!"),
250 _("Holding the old name."), MakeDisplayPath(oldname));
253 // now remove the oldname autosave file if existant!
254 removeAutosaveFile(oldname);
259 int MenuRunChktex(Buffer * buffer)
263 if (buffer->isSGML()) {
264 WriteAlert(_("Chktex does not work with SGML derived documents."));
267 ret = buffer->runChktex();
273 s = _("No warnings found.");
274 } else if (ret == 1) {
275 s = _("One warning found.");
276 t = _("Use 'Edit->Go to Error' to find it.");
279 s += _(" warnings found.");
280 t = _("Use 'Edit->Go to Error' to find them.");
282 WriteAlert(_("Chktex run successfully"), s, t);
284 WriteAlert(_("Error!"), _("It seems chktex does not work."));
292 lyxerr.debug() << "Running QuitLyX." << endl;
295 if (!bufferlist.QwriteAll())
298 lastfiles->writeFile(lyxrc.lastfiles);
301 // Set a flag that we do quitting from the program,
302 // so no refreshes are necessary.
305 // close buffers first
306 bufferlist.closeAll();
308 // do any other cleanup procedures now
309 lyxerr.debug() << "Deleting tmp dir " << system_tempdir << endl;
311 DestroyLyXTmpDir(system_tempdir);
318 void AutoSave(BufferView * bv)
319 // should probably be moved into BufferList (Lgb)
320 // Perfect target for a thread...
322 if (!bv->available())
325 if (bv->buffer()->isBakClean() || bv->buffer()->isReadonly()) {
326 // We don't save now, but we'll try again later
327 bv->owner()->resetAutosaveTimer();
331 bv->owner()->getMiniBuffer()->Set(_("Autosaving current document..."));
333 // create autosave filename
334 string fname = OnlyPath(bv->buffer()->fileName());
336 fname += OnlyFilename(bv->buffer()->fileName());
339 // tmp_ret will be located (usually) in /tmp
340 // will that be a problem?
341 pid_t const pid = fork(); // If you want to debug the autosave
342 // you should set pid to -1, and comment out the
344 if (pid == 0 || pid == -1) {
345 // pid = -1 signifies that lyx was unable
346 // to fork. But we will do the save
350 string const tmp_ret = lyx::tempName(string(), "lyxauto");
351 if (!tmp_ret.empty()) {
352 bv->buffer()->writeFile(tmp_ret, 1);
353 // assume successful write of tmp_ret
354 if (!lyx::rename(tmp_ret, fname)) {
356 // most likely couldn't move between filesystems
357 // unless write of tmp_ret failed
358 // so remove tmp file (if it exists)
359 lyx::unlink(tmp_ret);
366 // failed to write/rename tmp_ret so try writing direct
367 if (!bv->buffer()->writeFile(fname, 1)) {
368 // It is dangerous to do this in the child,
369 // but safe in the parent, so...
371 bv->owner()->getMiniBuffer()->Set(_("Autosave Failed!"));
374 if (pid == 0) { // we are the child so...
379 bv->buffer()->markBakClean();
380 bv->owner()->resetAutosaveTimer();
385 // Copyright CHT Software Service GmbH
388 // create new file with template
391 Buffer * NewLyxFile(string const & filename)
393 // Split argument by :
395 string tmpname = split(filename, name, ':');
396 #ifdef __EMX__ // Fix me! lyx_cb.C may not be low level enough to allow this.
397 if (name.length() == 1
398 && isalpha(static_cast<unsigned char>(name[0]))
399 && (prefixIs(tmpname, "/") || prefixIs(tmpname, "\\"))) {
401 name += token(tmpname, ':', 0);
402 tmpname = split(tmpname, ':');
405 lyxerr.debug() << "Arg is " << filename
406 << "\nName is " << name
407 << "\nTemplate is " << tmpname << endl;
409 // find a free buffer
410 Buffer * tmpbuf = bufferlist.newFile(name, tmpname);
412 lastfiles->newFile(tmpbuf->fileName());
417 // Insert ascii file (if filename is empty, prompt for one)
418 void InsertAsciiFile(BufferView * bv, string const & f, bool asParagraph)
423 if (!bv->available()) return;
427 fname = fileDlg.Select(_("File to Insert"),
428 bv->owner()->buffer()->filepath,
431 if (fname.empty()) return;
436 if (!fi.readable()) {
437 WriteFSAlert(_("Error! Specified file is unreadable: "),
438 MakeDisplayPath(fname, 50));
442 ifstream ifs(fname.c_str());
444 WriteFSAlert(_("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->InsertStringA(bv, tmpstr);
473 bv->text->InsertStringB(bv, tmpstr);
474 bv->update(bv->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
478 void MenuInsertLabel(BufferView * bv, string const & arg)
485 bv->text->cursor.par()->FirstPhysicalPar();
487 LyXParagraph * par = bv->text->cursor.par();
489 LyXLayout const * layout =
490 &textclasslist.Style(bv->buffer()->params.textclass,
493 if (layout->latextype == LATEX_PARAGRAPH && par->previous) {
495 LyXParagraph * par2 = par->previous->FirstPhysicalPar();
497 LyXParagraph * par2 = par->previous;
499 LyXLayout const * layout2 =
500 &textclasslist.Style(bv->buffer()->params.textclass,
502 if (layout2->latextype != LATEX_PARAGRAPH) {
507 string text = layout->latexname().substr(0, 3);
508 if (layout->latexname() == "theorem")
509 text = "thm"; // Create a correct prefix for prettyref
511 if (par->footnoteflag==LyXParagraph::OPEN_FOOTNOTE)
512 switch (par->footnotekind) {
513 case LyXParagraph::FIG:
514 case LyXParagraph::WIDE_FIG:
517 case LyXParagraph::TAB:
518 case LyXParagraph::WIDE_TAB:
521 case LyXParagraph::ALGORITHM:
524 case LyXParagraph::FOOTNOTE:
525 case LyXParagraph::MARGIN:
530 if (layout->latextype == LATEX_PARAGRAPH ||
531 lyxrc.label_init_length < 0)
533 string par_text = par->String(bv->buffer(), false);
534 for (int i = 0; i < lyxrc.label_init_length; ++i) {
535 if (par_text.empty())
538 par_text = split(par_text, head, ' ');
540 text += '-'; // Is it legal to use spaces in
545 pair<bool, string> result =
546 askForText(_("Enter new label to insert:"), text);
548 label = frontStrip(strip(result.second));
551 if (!label.empty()) {
552 InsetCommandParams p( "label", label );
553 InsetLabel * inset = new InsetLabel( p );
554 bv->insertInset( inset );
560 // This is _only_ used in Toolbar_pimpl.C, move it there and get rid of
561 // current_view. (Lgb)
562 void LayoutsCB(int sel, void *, Combox *)
564 string const tmp = tostr(sel);
565 current_view->owner()->getLyXFunc()->Dispatch(LFUN_LAYOUTNO,
570 void MenuLayoutSave(BufferView * bv)
572 if (!bv->available())
575 if (AskQuestion(_("Do you want to save the current settings"),
576 _("for Character, Document, Paper and Quotes"),
577 _("as default for new documents?")))
578 bv->buffer()->saveParamsAsDefaults();
584 if (fd_form_figure->form_figure->visible) {
585 fl_raise_form(fd_form_figure->form_figure);
587 fl_show_form(fd_form_figure->form_figure,
588 FL_PLACE_MOUSE | FL_FREE_SIZE, FL_TRANSIENT,
594 /* callbacks for form form_figure */
596 void FigureApplyCB(FL_OBJECT *, long)
598 if (!current_view->available())
601 Buffer * buffer = current_view->buffer();
602 if (buffer->isReadonly()) // paranoia
605 current_view->owner()->getMiniBuffer()->Set(_("Inserting figure..."));
606 if (fl_get_button(fd_form_figure->radio_inline)) {
607 InsetFig * new_inset = new InsetFig(100, 20, *buffer);
608 current_view->insertInset(new_inset);
609 current_view->owner()->getMiniBuffer()->Set(_("Figure inserted"));
610 new_inset->Edit(current_view, 0, 0, 0);
614 current_view->hideCursor();
615 current_view->update(current_view->text, BufferView::SELECT|BufferView::FITCUR);
616 current_view->beforeChange(current_view->text);
618 current_view->text->SetCursorParUndo(current_view->buffer());
619 current_view->text->FreezeUndo();
621 current_view->text->BreakParagraph(current_view);
622 current_view->update(current_view->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
624 if (current_view->text->cursor.par()->Last()) {
625 current_view->text->CursorLeft(current_view);
627 current_view->text->BreakParagraph(current_view);
628 current_view->update(current_view->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
631 // The standard layout should always be numer 0;
632 current_view->text->SetLayout(current_view, 0);
635 if (current_view->text->cursor.par()->footnoteflag ==
636 LyXParagraph::NO_FOOTNOTE) {
639 SetParagraph(current_view, 0, 0,
641 VSpace (0.3 * buffer->params.spacing.getValue(),
644 buffer->params.spacing.getValue(),
646 LYX_ALIGN_CENTER, string(), 0);
649 current_view->text->SetParagraph(current_view, 0, 0,
651 VSpace(VSpace::NONE),
652 VSpace(VSpace::NONE),
659 current_view->update(current_view->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
661 Inset * new_inset = new InsetFig(100, 100, *buffer);
662 current_view->insertInset(new_inset);
663 new_inset->Edit(current_view, 0, 0, 0);
664 current_view->update(current_view->text, BufferView::SELECT|BufferView::FITCUR);
665 current_view->owner()->getMiniBuffer()->Set(_("Figure inserted"));
666 current_view->text->UnFreezeUndo();
667 current_view->setState();
671 extern "C" void FigureCancelCB(FL_OBJECT *, long)
673 fl_hide_form(fd_form_figure->form_figure);
677 extern "C" void FigureOKCB(FL_OBJECT * ob, long data)
679 FigureApplyCB(ob, data);
680 FigureCancelCB(ob, data);
684 // This function runs "configure" and then rereads lyx.defaults to
685 // reconfigure the automatic settings.
686 void Reconfigure(BufferView * bv)
688 bv->owner()->getMiniBuffer()->Set(_("Running configure..."));
690 // Run configure in user lyx directory
692 Systemcalls one(Systemcalls::System,
693 AddName(system_lyxdir, "configure"));
695 bv->owner()->getMiniBuffer()->Set(_("Reloading configuration..."));
696 lyxrc.read(LibFileSearch(string(), "lyxrc.defaults"));
697 WriteAlert(_("The system has been reconfigured."),
698 _("You need to restart LyX to make use of any"),
699 _("updated document class specifications."));