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
19 #include "layout_forms.h"
22 #include "insets/insetlabel.h"
23 #include "insets/figinset.h"
25 #include "minibuffer.h"
27 #include "bufferlist.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;
52 extern Combox * combo_language;
53 extern Combox * combo_language2;
54 extern BufferList bufferlist;
55 extern void show_symbols_form();
56 extern FD_form_character * fd_form_character;
57 extern FD_form_figure * fd_form_figure;
59 extern BufferView * current_view; // called too many times in this file...
61 extern void DeleteSimpleCutBuffer(); /* for the cleanup when exiting */
63 extern void MenuSendto();
65 // this should be static, but I need it in buffer.C
66 bool quitting; // flag, that we are quitting the program
67 extern bool finished; // all cleanup done just let it run through now.
69 char ascii_type; /* for selection notify callbacks */
71 bool scrolling = false;
73 // This is used to make the dreaded font toggle problem hopefully go
74 // away. Definitely not the best solution, but I think it sorta works.
75 bool toggleall = true;
78 This is the inset locking stuff needed for mathed --------------------
80 an inset can simple call LockInset in it's edit call and *ONLY* in it's
82 Inset::Edit() can only be called by the main lyx module.
84 Then the inset may modify the menu's and/or iconbars.
86 Unlocking is either done by LyX or the inset itself with a UnlockInset-call
88 During the lock, all button and keyboard events will be modified
89 and send to the inset through the following inset-features. Note that
90 Inset::InsetUnlock will be called from inside UnlockInset. It is meant
91 to contain the code for restoring the menus and things like this.
94 virtual void InsetButtonPress(int x, int y, int button);
95 virtual void InsetButtonRelease(int x, int y, int button);
96 virtual void InsetKeyPress(XKeyEvent *ev);
97 virtual void InsetMotionNotify(int x, int y, int state);
98 virtual void InsetUnlock();
100 If a inset wishes any redraw and/or update it just has to call
102 It's is completly irrelevant, where the inset is. UpdateInset will
103 find it in any paragraph in any buffer.
104 Of course the_locking_inset and the insets in the current paragraph/buffer
105 are checked first, so no performance problem should occur.
107 Hope that's ok for the beginning, Alejandro,
108 sorry that I needed so much time,
113 //void UpdateInset(BufferView * bv, Inset * inset, bool mark_dirty = true);
115 /* these functions return 1 if an error occured,
117 // Now they work only for updatable insets. [Alejandro 080596]
118 //int LockInset(UpdatableInset * inset);
119 void ToggleLockedInsetCursor(int x, int y, int asc, int desc);
120 //void FitLockedInsetCursor(long x, long y, int asc, int desc);
121 //int UnlockInset(UpdatableInset * inset);
122 //void LockedInsetStoreUndo(Undo::undo_kind kind);
124 /* this is for asyncron updating. UpdateInsetUpdateList will be called
125 automatically from LyX. Just insert the Inset into the Updatelist */
126 //void UpdateInsetUpdateList();
127 //void PutInsetIntoInsetUpdateList(Inset * inset);
129 //InsetUpdateStruct * InsetUpdateList = 0;
133 -----------------------------------------------------------------------
137 void ShowMessage(Buffer const * buf,
140 string const & msg3, int delay)
143 buf->getUser()->owner()->getMiniBuffer()->Set(msg1, msg2,
146 lyxerr << msg1 << msg2 << msg3 << endl;
158 // should be moved to lyxfunc.C
159 bool MenuWrite(BufferView * bv, Buffer * buffer)
161 XFlush(fl_get_display());
162 if (!buffer->save()) {
163 string const fname = buffer->fileName();
164 string const s = MakeAbsPath(fname);
165 if (AskQuestion(_("Save failed. Rename and try again?"),
166 MakeDisplayPath(s, 50),
167 _("(If not, document is not saved.)"))) {
168 return MenuWriteAs(bv, buffer);
172 lastfiles->newFile(buffer->fileName());
178 // should be moved to BufferView.C
179 // Half of this func should be in LyXView, the rest in BufferView.
180 bool MenuWriteAs(BufferView * bv, Buffer * buffer)
182 // Why do we require BufferView::text to be able to write a
183 // document? I see no point in that. (Lgb)
184 //if (!bv->text) return;
186 string fname = buffer->fileName();
187 string oldname = fname;
191 fileDlg.SetButton(0, _("Documents"), lyxrc.document_path);
192 fileDlg.SetButton(1, _("Templates"), lyxrc.template_path);
194 if (!IsLyXFilename(fname))
197 fname = fileDlg.Select(_("Enter Filename to Save Document as"),
200 OnlyFilename(fname));
207 // Make sure the absolute filename ends with appropriate suffix
208 string s = MakeAbsPath(fname);
209 if (!IsLyXFilename(s))
212 // Same name as we have already?
213 if (!buffer->isUnnamed() && s == oldname) {
214 if (!AskQuestion(_("Same name as document already has:"),
215 MakeDisplayPath(s, 50),
218 // Falls through to name change and save
220 // No, but do we have another file with this name open?
221 else if (!buffer->isUnnamed() && bufferlist.exists(s)) {
222 if (AskQuestion(_("Another document with same name open!"),
223 MakeDisplayPath(s, 50),
224 _("Replace with current document?")))
226 bufferlist.close(bufferlist.getBuffer(s));
228 // Ok, change the name of the buffer, but don't save!
229 buffer->setFileName(s);
232 ShowMessage(buffer, _("Document renamed to '"),
233 MakeDisplayPath(s), _("', but not saved..."));
236 } // Check whether the file exists
238 FileInfo const myfile(s);
239 if (myfile.isOK() && !AskQuestion(_("Document already exists:"),
240 MakeDisplayPath(s, 50),
245 // Ok, change the name of the buffer
246 buffer->setFileName(s);
248 bool unnamed = buffer->isUnnamed();
249 buffer->setUnnamed(false);
251 // Small bug: If the save fails, we have irreversible changed the name
253 // Hope this is fixed this way! (Jug)
254 if (!MenuWrite(bv, buffer)) {
255 buffer->setFileName(oldname);
256 buffer->setUnnamed(unnamed);
257 ShowMessage(buffer, _("Document could not be saved!"),
258 _("Holding the old name."), MakeDisplayPath(oldname));
261 // now remove the oldname autosave file if existant!
262 removeAutosaveFile(oldname);
267 int MenuRunChktex(Buffer * buffer)
271 if (buffer->isSGML()) {
272 WriteAlert(_("Chktex does not work with SGML derived documents."));
275 ret = buffer->runChktex();
281 s = _("No warnings found.");
282 } else if (ret == 1) {
283 s = _("One warning found.");
284 t = _("Use 'Edit->Go to Error' to find it.");
287 s += _(" warnings found.");
288 t = _("Use 'Edit->Go to Error' to find them.");
290 WriteAlert(_("Chktex run successfully"), s, t);
292 WriteAlert(_("Error!"), _("It seems chktex does not work."));
300 lyxerr.debug() << "Running QuitLyX." << endl;
303 if (!bufferlist.QwriteAll())
306 lastfiles->writeFile(lyxrc.lastfiles);
309 // Set a flag that we do quitting from the program,
310 // so no refreshes are necessary.
313 // close buffers first
314 bufferlist.closeAll();
316 // do any other cleanup procedures now
317 lyxerr.debug() << "Deleting tmp dir " << system_tempdir << endl;
319 DestroyLyXTmpDir(system_tempdir);
326 void AutoSave(BufferView * bv)
327 // should probably be moved into BufferList (Lgb)
328 // Perfect target for a thread...
330 if (!bv->available())
333 if (bv->buffer()->isBakClean() || bv->buffer()->isReadonly()) {
334 // We don't save now, but we'll try again later
335 bv->owner()->resetAutosaveTimer();
339 bv->owner()->getMiniBuffer()->Set(_("Autosaving current document..."));
341 // create autosave filename
342 string fname = OnlyPath(bv->buffer()->fileName());
344 fname += OnlyFilename(bv->buffer()->fileName());
347 // tmp_ret will be located (usually) in /tmp
348 // will that be a problem?
349 pid_t const pid = fork(); // If you want to debug the autosave
350 // you should set pid to -1, and comment out the
352 if (pid == 0 || pid == -1) {
353 // pid = -1 signifies that lyx was unable
354 // to fork. But we will do the save
358 string const tmp_ret = lyx::tempName(string(), "lyxauto");
359 if (!tmp_ret.empty()) {
360 bv->buffer()->writeFile(tmp_ret, 1);
361 // assume successful write of tmp_ret
362 if (!lyx::rename(tmp_ret, fname)) {
364 // most likely couldn't move between filesystems
365 // unless write of tmp_ret failed
366 // so remove tmp file (if it exists)
367 lyx::unlink(tmp_ret);
374 // failed to write/rename tmp_ret so try writing direct
375 if (!bv->buffer()->writeFile(fname, 1)) {
376 // It is dangerous to do this in the child,
377 // but safe in the parent, so...
379 bv->owner()->getMiniBuffer()->Set(_("Autosave Failed!"));
382 if (pid == 0) { // we are the child so...
387 bv->buffer()->markBakClean();
388 bv->owner()->resetAutosaveTimer();
393 // Copyright CHT Software Service GmbH
396 // create new file with template
399 Buffer * NewLyxFile(string const & filename)
401 // Split argument by :
403 string tmpname = split(filename, name, ':');
404 #ifdef __EMX__ // Fix me! lyx_cb.C may not be low level enough to allow this.
405 if (name.length() == 1
406 && isalpha(static_cast<unsigned char>(name[0]))
407 && (prefixIs(tmpname, "/") || prefixIs(tmpname, "\\"))) {
409 name += token(tmpname, ':', 0);
410 tmpname = split(tmpname, ':');
413 lyxerr.debug() << "Arg is " << filename
414 << "\nName is " << name
415 << "\nTemplate is " << tmpname << endl;
417 // find a free buffer
418 Buffer * tmpbuf = bufferlist.newFile(name, tmpname);
420 lastfiles->newFile(tmpbuf->fileName());
425 // Insert ascii file (if filename is empty, prompt for one)
426 void InsertAsciiFile(BufferView * bv, string const & f, bool asParagraph)
431 if (!bv->available()) return;
435 fname = fileDlg.Select(_("File to Insert"),
436 bv->owner()->buffer()->filepath,
439 if (fname.empty()) return;
444 if (!fi.readable()) {
445 WriteFSAlert(_("Error! Specified file is unreadable: "),
446 MakeDisplayPath(fname, 50));
450 ifstream ifs(fname.c_str());
452 WriteFSAlert(_("Error! Cannot open specified file: "),
453 MakeDisplayPath(fname, 50));
457 ifs.unsetf(ios::skipws);
458 istream_iterator<char> ii(ifs);
459 istream_iterator<char> end;
460 #if !defined(USE_INCLUDED_STRING) && !defined(STD_STRING_IS_GOOD)
461 // We use this until the compilers get better...
463 copy(ii, end, back_inserter(tmp));
464 string const tmpstr(tmp.begin(), tmp.end());
466 // This is what we want to use and what we will use once the
467 // compilers get good enough.
468 //string tmpstr(ii, end); // yet a reason for using std::string
469 // alternate approach to get the file into a string:
471 copy(ii, end, back_inserter(tmpstr));
476 // clear the selection
477 bv->beforeChange(bv->text);
479 bv->text->InsertStringA(bv, tmpstr);
481 bv->text->InsertStringB(bv, tmpstr);
482 bv->update(bv->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
486 void MenuInsertLabel(BufferView * bv, string const & arg)
493 bv->text->cursor.par()->FirstPhysicalPar();
495 LyXParagraph * par = bv->text->cursor.par();
497 LyXLayout const * layout =
498 &textclasslist.Style(bv->buffer()->params.textclass,
501 if (layout->latextype == LATEX_PARAGRAPH && par->previous) {
503 LyXParagraph * par2 = par->previous->FirstPhysicalPar();
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 // This is _only_ used in Toolbar_pimpl.C, move it there and get rid of
569 // current_view. (Lgb)
570 void LayoutsCB(int sel, void *, Combox *)
572 string const tmp = tostr(sel);
573 current_view->owner()->getLyXFunc()->Dispatch(LFUN_LAYOUTNO,
578 void MenuLayoutCharacter()
580 static int ow = -1, oh;
582 if (fd_form_character->form_character->visible) {
583 fl_raise_form(fd_form_character->form_character);
585 fl_show_form(fd_form_character->form_character,
586 FL_PLACE_MOUSE | FL_FREE_SIZE, FL_TRANSIENT,
587 _("Character Style"));
589 ow = fd_form_character->form_character->w;
590 oh = fd_form_character->form_character->h;
592 fl_set_form_minsize(fd_form_character->form_character, ow, oh);
598 void MenuLayoutSave(BufferView * bv)
600 if (!bv->available())
603 if (AskQuestion(_("Do you want to save the current settings"),
604 _("for Character, Document, Paper and Quotes"),
605 _("as default for new documents?")))
606 bv->buffer()->saveParamsAsDefaults();
610 // This is both GUI and LyXFont dependent. Don't know where to put it. (Asger)
611 // Well, it's mostly GUI dependent, so I guess it will stay here. (Asger)
612 LyXFont const UserFreeFont(BufferParams const & params)
614 LyXFont font(LyXFont::ALL_IGNORE);
616 int pos = fl_get_choice(fd_form_character->choice_family);
618 case 1: font.setFamily(LyXFont::IGNORE_FAMILY); break;
619 case 2: font.setFamily(LyXFont::ROMAN_FAMILY); break;
620 case 3: font.setFamily(LyXFont::SANS_FAMILY); break;
621 case 4: font.setFamily(LyXFont::TYPEWRITER_FAMILY); break;
622 case 5: font.setFamily(LyXFont::INHERIT_FAMILY); break;
625 pos = fl_get_choice(fd_form_character->choice_series);
627 case 1: font.setSeries(LyXFont::IGNORE_SERIES); break;
628 case 2: font.setSeries(LyXFont::MEDIUM_SERIES); break;
629 case 3: font.setSeries(LyXFont::BOLD_SERIES); break;
630 case 4: font.setSeries(LyXFont::INHERIT_SERIES); break;
633 pos = fl_get_choice(fd_form_character->choice_shape);
635 case 1: font.setShape(LyXFont::IGNORE_SHAPE); break;
636 case 2: font.setShape(LyXFont::UP_SHAPE); break;
637 case 3: font.setShape(LyXFont::ITALIC_SHAPE); break;
638 case 4: font.setShape(LyXFont::SLANTED_SHAPE); break;
639 case 5: font.setShape(LyXFont::SMALLCAPS_SHAPE); break;
640 case 6: font.setShape(LyXFont::INHERIT_SHAPE); break;
643 pos = fl_get_choice(fd_form_character->choice_size);
645 case 1: font.setSize(LyXFont::IGNORE_SIZE); break;
646 case 2: font.setSize(LyXFont::SIZE_TINY); break;
647 case 3: font.setSize(LyXFont::SIZE_SCRIPT); break;
648 case 4: font.setSize(LyXFont::SIZE_FOOTNOTE); break;
649 case 5: font.setSize(LyXFont::SIZE_SMALL); break;
650 case 6: font.setSize(LyXFont::SIZE_NORMAL); break;
651 case 7: font.setSize(LyXFont::SIZE_LARGE); break;
652 case 8: font.setSize(LyXFont::SIZE_LARGER); break;
653 case 9: font.setSize(LyXFont::SIZE_LARGEST); break;
654 case 10: font.setSize(LyXFont::SIZE_HUGE); break;
655 case 11: font.setSize(LyXFont::SIZE_HUGER); break;
656 case 12: font.setSize(LyXFont::INCREASE_SIZE); break;
657 case 13: font.setSize(LyXFont::DECREASE_SIZE); break;
658 case 14: font.setSize(LyXFont::INHERIT_SIZE); break;
661 pos = fl_get_choice(fd_form_character->choice_bar);
663 case 1: font.setEmph(LyXFont::IGNORE);
664 font.setUnderbar(LyXFont::IGNORE);
665 font.setNoun(LyXFont::IGNORE);
666 font.setLatex(LyXFont::IGNORE);
668 case 2: font.setEmph(LyXFont::TOGGLE); break;
669 case 3: font.setUnderbar(LyXFont::TOGGLE); break;
670 case 4: font.setNoun(LyXFont::TOGGLE); break;
671 case 5: font.setLatex(LyXFont::TOGGLE); break;
672 case 6: font.setEmph(LyXFont::INHERIT);
673 font.setUnderbar(LyXFont::INHERIT);
674 font.setNoun(LyXFont::INHERIT);
675 font.setLatex(LyXFont::INHERIT);
679 pos = fl_get_choice(fd_form_character->choice_color);
681 case 1: font.setColor(LColor::ignore); break;
682 case 2: font.setColor(LColor::none); break;
683 case 3: font.setColor(LColor::black); break;
684 case 4: font.setColor(LColor::white); break;
685 case 5: font.setColor(LColor::red); break;
686 case 6: font.setColor(LColor::green); break;
687 case 7: font.setColor(LColor::blue); break;
688 case 8: font.setColor(LColor::cyan); break;
689 case 9: font.setColor(LColor::magenta); break;
690 case 10: font.setColor(LColor::yellow); break;
691 case 11: font.setColor(LColor::inherit); break;
694 int const choice = combo_language2->get();
696 font.setLanguage(ignore_language);
697 else if (choice == 2)
698 font.setLanguage(params.language);
700 font.setLanguage(languages.getLanguage(combo_language2->getline()));
706 /* callbacks for form form_character */
709 void CharacterApplyCB(FL_OBJECT *, long)
711 // we set toggleall locally here, since it should be true for
712 // all other uses of ToggleAndShow() (JMarc)
713 toggleall = fl_get_button(fd_form_character->check_toggle_all);
714 ToggleAndShow(current_view, UserFreeFont(current_view->buffer()->params));
715 current_view->setState();
721 void CharacterCloseCB(FL_OBJECT *, long)
723 fl_hide_form(fd_form_character->form_character);
728 void CharacterOKCB(FL_OBJECT * ob, long data)
730 CharacterApplyCB(ob, data);
731 CharacterCloseCB(ob, data);
738 if (fd_form_figure->form_figure->visible) {
739 fl_raise_form(fd_form_figure->form_figure);
741 fl_show_form(fd_form_figure->form_figure,
742 FL_PLACE_MOUSE | FL_FREE_SIZE, FL_TRANSIENT,
748 /* callbacks for form form_figure */
750 void FigureApplyCB(FL_OBJECT *, long)
752 if (!current_view->available())
755 Buffer * buffer = current_view->buffer();
756 if (buffer->isReadonly()) // paranoia
759 current_view->owner()->getMiniBuffer()->Set(_("Inserting figure..."));
760 if (fl_get_button(fd_form_figure->radio_inline)) {
761 InsetFig * new_inset = new InsetFig(100, 20, *buffer);
762 current_view->insertInset(new_inset);
763 current_view->owner()->getMiniBuffer()->Set(_("Figure inserted"));
764 new_inset->Edit(current_view, 0, 0, 0);
768 current_view->hideCursor();
769 current_view->update(current_view->text, BufferView::SELECT|BufferView::FITCUR);
770 current_view->beforeChange(current_view->text);
772 current_view->text->SetCursorParUndo(current_view->buffer());
773 current_view->text->FreezeUndo();
775 current_view->text->BreakParagraph(current_view);
776 current_view->update(current_view->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
778 if (current_view->text->cursor.par()->Last()) {
779 current_view->text->CursorLeft(current_view);
781 current_view->text->BreakParagraph(current_view);
782 current_view->update(current_view->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
785 // The standard layout should always be numer 0;
786 current_view->text->SetLayout(current_view, 0);
789 if (current_view->text->cursor.par()->footnoteflag ==
790 LyXParagraph::NO_FOOTNOTE) {
793 SetParagraph(current_view, 0, 0,
795 VSpace (0.3 * buffer->params.spacing.getValue(),
798 buffer->params.spacing.getValue(),
800 LYX_ALIGN_CENTER, string(), 0);
803 current_view->text->SetParagraph(current_view, 0, 0,
805 VSpace(VSpace::NONE),
806 VSpace(VSpace::NONE),
813 current_view->update(current_view->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
815 Inset * new_inset = new InsetFig(100, 100, *buffer);
816 current_view->insertInset(new_inset);
817 new_inset->Edit(current_view, 0, 0, 0);
818 current_view->update(current_view->text, BufferView::SELECT|BufferView::FITCUR);
819 current_view->owner()->getMiniBuffer()->Set(_("Figure inserted"));
820 current_view->text->UnFreezeUndo();
821 current_view->setState();
825 extern "C" void FigureCancelCB(FL_OBJECT *, long)
827 fl_hide_form(fd_form_figure->form_figure);
831 extern "C" void FigureOKCB(FL_OBJECT * ob, long data)
833 FigureApplyCB(ob, data);
834 FigureCancelCB(ob, data);
838 // This function runs "configure" and then rereads lyx.defaults to
839 // reconfigure the automatic settings.
840 void Reconfigure(BufferView * bv)
842 bv->owner()->getMiniBuffer()->Set(_("Running configure..."));
844 // Run configure in user lyx directory
846 Systemcalls one(Systemcalls::System,
847 AddName(system_lyxdir, "configure"));
849 bv->owner()->getMiniBuffer()->Set(_("Reloading configuration..."));
850 lyxrc.read(LibFileSearch(string(), "lyxrc.defaults"));
851 WriteAlert(_("The system has been reconfigured."),
852 _("You need to restart LyX to make use of any"),
853 _("updated document class specifications."));