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"
43 using std::back_inserter;
44 using std::istream_iterator;
48 extern BufferList bufferlist;
49 extern FD_form_figure * fd_form_figure;
51 extern BufferView * current_view; // called too many times in this file...
53 // this should be static, but I need it in buffer.C
54 bool quitting; // flag, that we are quitting the program
55 extern bool finished; // all cleanup done just let it run through now.
58 This is the inset locking stuff needed for mathed --------------------
60 an inset can simple call LockInset in it's edit call and *ONLY* in it's
62 Inset::Edit() can only be called by the main lyx module.
64 Then the inset may modify the menu's and/or iconbars.
66 Unlocking is either done by LyX or the inset itself with a UnlockInset-call
68 During the lock, all button and keyboard events will be modified
69 and send to the inset through the following inset-features. Note that
70 Inset::InsetUnlock will be called from inside UnlockInset. It is meant
71 to contain the code for restoring the menus and things like this.
74 virtual void InsetButtonPress(int x, int y, int button);
75 virtual void InsetButtonRelease(int x, int y, int button);
76 virtual void InsetKeyPress(XKeyEvent *ev);
77 virtual void InsetMotionNotify(int x, int y, int state);
78 virtual void InsetUnlock();
80 If a inset wishes any redraw and/or update it just has to call
82 It's is completly irrelevant, where the inset is. UpdateInset will
83 find it in any paragraph in any buffer.
84 Of course the_locking_inset and the insets in the current paragraph/buffer
85 are checked first, so no performance problem should occur.
87 Hope that's ok for the beginning, Alejandro,
88 sorry that I needed so much time,
93 //void UpdateInset(BufferView * bv, Inset * inset, bool mark_dirty = true);
95 /* these functions return 1 if an error occured,
97 // Now they work only for updatable insets. [Alejandro 080596]
98 //int LockInset(UpdatableInset * inset);
99 void ToggleLockedInsetCursor(int x, int y, int asc, int desc);
100 //void FitLockedInsetCursor(long x, long y, int asc, int desc);
101 //int UnlockInset(UpdatableInset * inset);
102 //void LockedInsetStoreUndo(Undo::undo_kind kind);
104 /* this is for asyncron updating. UpdateInsetUpdateList will be called
105 automatically from LyX. Just insert the Inset into the Updatelist */
106 //void UpdateInsetUpdateList();
107 //void PutInsetIntoInsetUpdateList(Inset * inset);
109 //InsetUpdateStruct * InsetUpdateList = 0;
113 -----------------------------------------------------------------------
117 void ShowMessage(Buffer const * buf,
120 string const & msg3, int delay)
123 buf->getUser()->owner()->getMiniBuffer()->Set(msg1, msg2,
126 lyxerr << msg1 << msg2 << msg3 << endl;
137 // should be moved to lyxfunc.C
138 bool MenuWrite(BufferView * bv, Buffer * buffer)
141 XFlush(fl_get_display());
143 if (!buffer->save()) {
144 string const fname = buffer->fileName();
145 string const s = MakeAbsPath(fname);
146 if (AskQuestion(_("Save failed. Rename and try again?"),
147 MakeDisplayPath(s, 50),
148 _("(If not, document is not saved.)"))) {
149 return WriteAs(bv, buffer);
153 lastfiles->newFile(buffer->fileName());
159 // should be moved to BufferView.C
160 // Half of this func should be in LyXView, the rest in BufferView.
161 bool WriteAs(BufferView * bv, Buffer * buffer, string const & filename)
163 string fname = buffer->fileName();
164 string oldname = fname;
166 if (filename.empty()) {
168 FileDialog fileDlg(bv->owner(),
169 _("Choose a filename to save document as"),
171 make_pair(string(_("Documents")),
172 string(lyxrc.document_path)),
173 make_pair(string(_("Templates")),
174 string(lyxrc.template_path)));
176 if (!IsLyXFilename(fname))
179 FileDialog::Result result =
180 fileDlg.Select(OnlyPath(fname),
181 _("*.lyx|LyX Documents (*.lyx)"),
182 OnlyFilename(fname));
184 if (result.first == FileDialog::Later)
187 fname = result.second;
192 // Make sure the absolute filename ends with appropriate suffix
193 fname = MakeAbsPath(fname);
194 if (!IsLyXFilename(fname))
199 // Same name as we have already?
200 if (!buffer->isUnnamed() && fname == oldname) {
201 if (!AskQuestion(_("Same name as document already has:"),
202 MakeDisplayPath(fname, 50),
205 // Falls through to name change and save
207 // No, but do we have another file with this name open?
208 else if (!buffer->isUnnamed() && bufferlist.exists(fname)) {
209 if (AskQuestion(_("Another document with same name open!"),
210 MakeDisplayPath(fname, 50),
211 _("Replace with current document?")))
213 bufferlist.close(bufferlist.getBuffer(fname));
215 // Ok, change the name of the buffer, but don't save!
216 buffer->setFileName(fname);
219 ShowMessage(buffer, _("Document renamed to '"),
220 MakeDisplayPath(fname), _("', but not saved..."));
223 } // Check whether the file exists
225 FileInfo const myfile(fname);
226 if (myfile.isOK() && !AskQuestion(_("Document already exists:"),
227 MakeDisplayPath(fname, 50),
232 // Ok, change the name of the buffer
233 buffer->setFileName(fname);
235 bool unnamed = buffer->isUnnamed();
236 buffer->setUnnamed(false);
238 if (!MenuWrite(bv, buffer)) {
239 buffer->setFileName(oldname);
240 buffer->setUnnamed(unnamed);
241 ShowMessage(buffer, _("Document could not be saved!"),
242 _("Holding the old name."), MakeDisplayPath(oldname));
245 // now remove the oldname autosave file if existant!
246 removeAutosaveFile(oldname);
251 int MenuRunChktex(Buffer * buffer)
255 if (buffer->isSGML()) {
256 WriteAlert(_("Chktex does not work with SGML derived documents."));
259 ret = buffer->runChktex();
265 s = _("No warnings found.");
266 } else if (ret == 1) {
267 s = _("One warning found.");
268 t = _("Use 'Edit->Go to Error' to find it.");
271 s += _(" warnings found.");
272 t = _("Use 'Edit->Go to Error' to find them.");
274 WriteAlert(_("Chktex run successfully"), s, t);
276 WriteAlert(_("Error!"), _("It seems chktex does not work."));
284 lyxerr.debug() << "Running QuitLyX." << endl;
287 if (!bufferlist.QwriteAll())
290 lastfiles->writeFile(lyxrc.lastfiles);
293 // Set a flag that we do quitting from the program,
294 // so no refreshes are necessary.
297 // close buffers first
298 bufferlist.closeAll();
300 // do any other cleanup procedures now
301 lyxerr.debug() << "Deleting tmp dir " << system_tempdir << endl;
303 DestroyLyXTmpDir(system_tempdir);
310 void AutoSave(BufferView * bv)
311 // should probably be moved into BufferList (Lgb)
312 // Perfect target for a thread...
314 if (!bv->available())
317 if (bv->buffer()->isBakClean() || bv->buffer()->isReadonly()) {
318 // We don't save now, but we'll try again later
319 bv->owner()->resetAutosaveTimer();
323 bv->owner()->getMiniBuffer()->Set(_("Autosaving current document..."));
325 // create autosave filename
326 string fname = OnlyPath(bv->buffer()->fileName());
328 fname += OnlyFilename(bv->buffer()->fileName());
331 // tmp_ret will be located (usually) in /tmp
332 // will that be a problem?
333 pid_t const pid = fork(); // If you want to debug the autosave
334 // you should set pid to -1, and comment out the
336 if (pid == 0 || pid == -1) {
337 // pid = -1 signifies that lyx was unable
338 // to fork. But we will do the save
342 string const tmp_ret = lyx::tempName(string(), "lyxauto");
343 if (!tmp_ret.empty()) {
344 bv->buffer()->writeFile(tmp_ret, 1);
345 // assume successful write of tmp_ret
346 if (!lyx::rename(tmp_ret, fname)) {
348 // most likely couldn't move between filesystems
349 // unless write of tmp_ret failed
350 // so remove tmp file (if it exists)
351 lyx::unlink(tmp_ret);
358 // failed to write/rename tmp_ret so try writing direct
359 if (!bv->buffer()->writeFile(fname, 1)) {
360 // It is dangerous to do this in the child,
361 // but safe in the parent, so...
363 bv->owner()->getMiniBuffer()->Set(_("Autosave Failed!"));
366 if (pid == 0) { // we are the child so...
371 bv->buffer()->markBakClean();
372 bv->owner()->resetAutosaveTimer();
377 // Copyright CHT Software Service GmbH
380 // create new file with template
383 Buffer * NewLyxFile(string const & filename)
385 // Split argument by :
387 string tmpname = split(filename, name, ':');
388 #ifdef __EMX__ // Fix me! lyx_cb.C may not be low level enough to allow this.
389 if (name.length() == 1
390 && isalpha(static_cast<unsigned char>(name[0]))
391 && (prefixIs(tmpname, "/") || prefixIs(tmpname, "\\"))) {
393 name += token(tmpname, ':', 0);
394 tmpname = split(tmpname, ':');
397 lyxerr.debug() << "Arg is " << filename
398 << "\nName is " << name
399 << "\nTemplate is " << tmpname << endl;
401 // find a free buffer
402 Buffer * tmpbuf = bufferlist.newFile(name, tmpname);
404 lastfiles->newFile(tmpbuf->fileName());
409 // Insert ascii file (if filename is empty, prompt for one)
410 void InsertAsciiFile(BufferView * bv, string const & f, bool asParagraph)
414 if (!bv->available())
418 FileDialog fileDlg(bv->owner(), _("Select file to insert"),
419 (asParagraph) ? LFUN_FILE_INSERT_ASCII_PARA : LFUN_FILE_INSERT_ASCII);
421 FileDialog::Result result = fileDlg.Select(bv->owner()->buffer()->filepath);
423 if (result.first == FileDialog::Later)
426 fname = result.second;
434 if (!fi.readable()) {
435 WriteFSAlert(_("Error! Specified file is unreadable: "),
436 MakeDisplayPath(fname, 50));
440 ifstream ifs(fname.c_str());
442 WriteFSAlert(_("Error! Cannot open specified file: "),
443 MakeDisplayPath(fname, 50));
447 ifs.unsetf(ios::skipws);
448 istream_iterator<char> ii(ifs);
449 istream_iterator<char> end;
450 #if !defined(USE_INCLUDED_STRING) && !defined(STD_STRING_IS_GOOD)
451 // We use this until the compilers get better...
453 copy(ii, end, back_inserter(tmp));
454 string const tmpstr(tmp.begin(), tmp.end());
456 // This is what we want to use and what we will use once the
457 // compilers get good enough.
458 //string tmpstr(ii, end); // yet a reason for using std::string
459 // alternate approach to get the file into a string:
461 copy(ii, end, back_inserter(tmpstr));
466 // clear the selection
467 bv->beforeChange(bv->text);
469 bv->text->InsertStringA(bv, tmpstr);
471 bv->text->InsertStringB(bv, tmpstr);
472 bv->update(bv->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
476 void MenuInsertLabel(BufferView * bv, string const & arg)
483 bv->text->cursor.par()->FirstPhysicalPar();
485 LyXParagraph * par = bv->text->cursor.par();
487 LyXLayout const * layout =
488 &textclasslist.Style(bv->buffer()->params.textclass,
492 if (layout->latextype == LATEX_PARAGRAPH && par->previous_) {
493 LyXParagraph * par2 = par->previous_->FirstPhysicalPar();
495 if (layout->latextype == LATEX_PARAGRAPH && par->previous()) {
496 LyXParagraph * par2 = par->previous();
498 LyXLayout const * layout2 =
499 &textclasslist.Style(bv->buffer()->params.textclass,
501 if (layout2->latextype != LATEX_PARAGRAPH) {
506 string text = layout->latexname().substr(0, 3);
507 if (layout->latexname() == "theorem")
508 text = "thm"; // Create a correct prefix for prettyref
510 if (par->footnoteflag==LyXParagraph::OPEN_FOOTNOTE)
511 switch (par->footnotekind) {
512 case LyXParagraph::FIG:
513 case LyXParagraph::WIDE_FIG:
516 case LyXParagraph::TAB:
517 case LyXParagraph::WIDE_TAB:
520 case LyXParagraph::ALGORITHM:
523 case LyXParagraph::FOOTNOTE:
524 case LyXParagraph::MARGIN:
529 if (layout->latextype == LATEX_PARAGRAPH ||
530 lyxrc.label_init_length < 0)
532 string par_text = par->String(bv->buffer(), false);
533 for (int i = 0; i < lyxrc.label_init_length; ++i) {
534 if (par_text.empty())
537 par_text = split(par_text, head, ' ');
539 text += '-'; // Is it legal to use spaces in
544 pair<bool, string> result =
545 askForText(_("Enter new label to insert:"), text);
547 label = frontStrip(strip(result.second));
550 if (!label.empty()) {
551 InsetCommandParams p( "label", label );
552 InsetLabel * inset = new InsetLabel( p );
553 bv->insertInset( inset );
559 void MenuLayoutSave(BufferView * bv)
561 if (!bv->available())
564 if (AskQuestion(_("Do you want to save the current settings"),
565 _("for Character, Document, Paper and Quotes"),
566 _("as default for new documents?")))
567 bv->buffer()->saveParamsAsDefaults();
573 if (fd_form_figure->form_figure->visible) {
574 fl_raise_form(fd_form_figure->form_figure);
576 fl_show_form(fd_form_figure->form_figure,
577 FL_PLACE_MOUSE | FL_FREE_SIZE, FL_TRANSIENT,
583 /* callbacks for form form_figure */
585 void FigureApplyCB(FL_OBJECT *, long)
587 if (!current_view->available())
590 Buffer * buffer = current_view->buffer();
591 if (buffer->isReadonly()) // paranoia
594 current_view->owner()->getMiniBuffer()->Set(_("Inserting figure..."));
595 if (fl_get_button(fd_form_figure->radio_inline)) {
596 InsetFig * new_inset = new InsetFig(100, 20, *buffer);
597 current_view->insertInset(new_inset);
598 current_view->owner()->getMiniBuffer()->Set(_("Figure inserted"));
599 new_inset->Edit(current_view, 0, 0, 0);
603 current_view->hideCursor();
604 current_view->update(current_view->text, BufferView::SELECT|BufferView::FITCUR);
605 current_view->beforeChange(current_view->text);
607 current_view->text->SetCursorParUndo(current_view->buffer());
608 current_view->text->FreezeUndo();
610 current_view->text->BreakParagraph(current_view);
611 current_view->update(current_view->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
614 if (current_view->text->cursor.par()->Last()) {
616 if (current_view->text->cursor.par()->size()) {
618 current_view->text->CursorLeft(current_view);
620 current_view->text->BreakParagraph(current_view);
621 current_view->update(current_view->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
624 // The standard layout should always be numer 0;
625 current_view->text->SetLayout(current_view, 0);
628 if (current_view->text->cursor.par()->footnoteflag ==
629 LyXParagraph::NO_FOOTNOTE) {
632 SetParagraph(current_view, 0, 0,
634 VSpace (0.3 * buffer->params.spacing.getValue(),
637 buffer->params.spacing.getValue(),
639 LYX_ALIGN_CENTER, string(), 0);
642 current_view->text->SetParagraph(current_view, 0, 0,
644 VSpace(VSpace::NONE),
645 VSpace(VSpace::NONE),
652 current_view->update(current_view->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
654 Inset * new_inset = new InsetFig(100, 100, *buffer);
655 current_view->insertInset(new_inset);
656 new_inset->Edit(current_view, 0, 0, 0);
657 current_view->update(current_view->text, BufferView::SELECT|BufferView::FITCUR);
658 current_view->owner()->getMiniBuffer()->Set(_("Figure inserted"));
659 current_view->text->UnFreezeUndo();
660 current_view->setState();
665 void FigureCancelCB(FL_OBJECT *, long)
667 fl_hide_form(fd_form_figure->form_figure);
672 void FigureOKCB(FL_OBJECT * ob, long data)
674 FigureApplyCB(ob, data);
675 FigureCancelCB(ob, data);
679 // This function runs "configure" and then rereads lyx.defaults to
680 // reconfigure the automatic settings.
681 void Reconfigure(BufferView * bv)
683 bv->owner()->getMiniBuffer()->Set(_("Running configure..."));
685 // Run configure in user lyx directory
687 Systemcalls one(Systemcalls::System,
688 AddName(system_lyxdir, "configure"));
690 bv->owner()->getMiniBuffer()->Set(_("Reloading configuration..."));
691 lyxrc.read(LibFileSearch(string(), "lyxrc.defaults"));
692 WriteAlert(_("The system has been reconfigured."),
693 _("You need to restart LyX to make use of any"),
694 _("updated document class specifications."));