]> git.lyx.org Git - lyx.git/blob - src/lyx_cb.C
Added splash for qt2 frontend, Edwin
[lyx.git] / src / lyx_cb.C
1 /* This file is part of
2  * ====================================================== 
3  * 
4  *           LyX, The Document Processor
5  *       
6  *          Copyright 1995 Matthias Ettrich,
7  *          Copyright 1995-2000 The LyX Team.
8  *
9  * ====================================================== */
10
11 #include <config.h>
12
13 #include <fstream>
14 #include <algorithm>
15 #include <utility> 
16 #include <iostream>
17
18 #include FORMS_H_LOCATION
19 #include "lyx.h"
20 #include "lyx_main.h"
21 #include "lyx_cb.h"
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"
28 #include "LyXView.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"
35 #include "lyxrc.h"
36 #include "lyxtext.h"
37
38 using std::vector;
39 using std::ifstream;
40 using std::copy;
41 using std::endl;
42 using std::ios;
43 using std::back_inserter;
44 using std::istream_iterator;
45 using std::pair;
46 using std::make_pair;
47
48 extern BufferList bufferlist;
49 extern FD_form_figure * fd_form_figure;
50
51 extern BufferView * current_view; // called too many times in this file...
52
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.
56
57 /* 
58    This is the inset locking stuff needed for mathed --------------------
59
60    an inset can simple call LockInset in it's edit call and *ONLY* in it's
61    edit call.
62    Inset::Edit() can only be called by the main lyx module.
63
64    Then the inset may modify the menu's and/or iconbars. 
65
66    Unlocking is either done by LyX or the inset itself with a UnlockInset-call
67
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.
72
73    
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();
79
80    If a inset wishes any redraw and/or update it just has to call
81    UpdateInset(this).
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.
86    
87    Hope that's ok for the beginning, Alejandro,
88    sorry that I needed so much time,
89
90                   Matthias
91    */
92
93 //void UpdateInset(BufferView * bv, Inset * inset, bool mark_dirty = true);
94
95 /* these functions return 1 if an error occured, 
96    otherwise 0 */
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);
103
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);
108
109 //InsetUpdateStruct * InsetUpdateList = 0;
110
111
112 /*
113   -----------------------------------------------------------------------
114  */
115
116
117 void ShowMessage(Buffer const * buf,
118                  string const & msg1,
119                  string const & msg2,
120                  string const & msg3, int delay)
121 {
122         if (lyxrc.use_gui)
123                 buf->getUser()->owner()->getMiniBuffer()->Set(msg1, msg2,
124                                                               msg3, delay);
125         else
126                 lyxerr << msg1 << msg2 << msg3 << endl;
127 }
128
129
130 //
131 // Menu callbacks
132 //
133
134 //
135 // File menu
136 //
137 // should be moved to lyxfunc.C
138 bool MenuWrite(BufferView * bv, Buffer * buffer)
139 {
140         // FIXME: needed ?
141         XFlush(fl_get_display());
142  
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);
150                 }
151                 return false;
152         } else
153                 lastfiles->newFile(buffer->fileName());
154         return true;
155 }
156
157
158
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)
162 {
163         string fname = buffer->fileName();
164         string oldname = fname;
165
166         if (filename.empty()) {
167
168                 FileDialog fileDlg(bv->owner(),
169                                    _("Choose a filename to save document as"),
170                         LFUN_WRITEAS,
171                         make_pair(string(_("Documents")),
172                                   string(lyxrc.document_path)),
173                         make_pair(string(_("Templates")),
174                                   string(lyxrc.template_path)));
175
176                 if (!IsLyXFilename(fname))
177                         fname += ".lyx";
178
179                 FileDialog::Result result =
180                         fileDlg.Select(OnlyPath(fname),
181                                        _("*.lyx|LyX Documents (*.lyx)"),
182                                        OnlyFilename(fname));
183
184                 if (result.first == FileDialog::Later)
185                         return false;
186
187                 fname = result.second;
188
189                 if (fname.empty())
190                         return false;
191
192                 // Make sure the absolute filename ends with appropriate suffix
193                 fname = MakeAbsPath(fname);
194                 if (!IsLyXFilename(fname))
195                         fname += ".lyx";
196         } else
197                 fname = filename;
198
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),
203                                  _("Save anyway?")))
204                         return false;
205                 // Falls through to name change and save
206         } 
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?")))
212                         {
213                                 bufferlist.close(bufferlist.getBuffer(fname));
214
215                                 // Ok, change the name of the buffer, but don't save!
216                                 buffer->setFileName(fname);
217                                 buffer->markDirty();
218
219                                 ShowMessage(buffer, _("Document renamed to '"),
220                                                 MakeDisplayPath(fname), _("', but not saved..."));
221                 }
222                 return false;
223         } // Check whether the file exists
224         else {
225                 FileInfo const myfile(fname);
226                 if (myfile.isOK() && !AskQuestion(_("Document already exists:"), 
227                                                   MakeDisplayPath(fname, 50),
228                                                   _("Replace file?")))
229                         return false;
230         }
231
232         // Ok, change the name of the buffer
233         buffer->setFileName(fname);
234         buffer->markDirty();
235         bool unnamed = buffer->isUnnamed();
236         buffer->setUnnamed(false);
237
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));
243             return false;
244         }
245         // now remove the oldname autosave file if existant!
246         removeAutosaveFile(oldname);
247         return true;
248 }
249
250
251 int MenuRunChktex(Buffer * buffer)
252 {
253         int ret;
254
255         if (buffer->isSGML()) {
256                 WriteAlert(_("Chktex does not work with SGML derived documents."));
257                 return 0;
258         } else 
259                 ret = buffer->runChktex();
260    
261         if (ret >= 0) {
262                 string s;
263                 string t;
264                 if (ret == 0) {
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.");
269                 } else {
270                         s += tostr(ret);
271                         s += _(" warnings found.");
272                         t = _("Use 'Edit->Go to Error' to find them.");
273                 }
274                 WriteAlert(_("Chktex run successfully"), s, t);
275         } else {
276                 WriteAlert(_("Error!"), _("It seems chktex does not work."));
277         }
278         return ret;
279 }
280
281
282 void QuitLyX()
283 {
284         lyxerr.debug() << "Running QuitLyX." << endl;
285
286         if (lyxrc.use_gui) {
287                 if (!bufferlist.QwriteAll())
288                         return;
289
290                 lastfiles->writeFile(lyxrc.lastfiles);
291         }
292
293         // Set a flag that we do quitting from the program,
294         // so no refreshes are necessary.
295         quitting = true;
296
297         // close buffers first
298         bufferlist.closeAll();
299
300         // do any other cleanup procedures now
301         lyxerr.debug() << "Deleting tmp dir " << system_tempdir << endl;
302
303         DestroyLyXTmpDir(system_tempdir);
304
305         finished = true;
306 }
307
308
309
310 void AutoSave(BufferView * bv)
311         // should probably be moved into BufferList (Lgb)
312         // Perfect target for a thread...
313 {
314         if (!bv->available())
315                 return;
316
317         if (bv->buffer()->isBakClean() || bv->buffer()->isReadonly()) {
318                 // We don't save now, but we'll try again later
319                 bv->owner()->resetAutosaveTimer();
320                 return;
321         }
322
323         bv->owner()->getMiniBuffer()->Set(_("Autosaving current document..."));
324         
325         // create autosave filename
326         string fname =  OnlyPath(bv->buffer()->fileName());
327         fname += "#";
328         fname += OnlyFilename(bv->buffer()->fileName());
329         fname += "#";
330         
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
335         // fork.
336         if (pid == 0 || pid == -1) {
337                 // pid = -1 signifies that lyx was unable
338                 // to fork. But we will do the save
339                 // anyway.
340                 bool failed = false;
341                 
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)) {
347                                 failed = true;
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);
352                         }
353                 } else {
354                         failed = true;
355                 }
356                 
357                 if (failed) {
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...
362                                 if (pid == -1)
363                                         bv->owner()->getMiniBuffer()->Set(_("Autosave Failed!"));
364                         }
365                 }
366                 if (pid == 0) { // we are the child so...
367                         _exit(0);
368                 }
369         }
370         
371         bv->buffer()->markBakClean();
372         bv->owner()->resetAutosaveTimer();
373 }
374
375
376 //
377 // Copyright CHT Software Service GmbH
378 // Uwe C. Schroeder
379 //
380 // create new file with template
381 // SERVERCMD !
382 //
383 Buffer * NewLyxFile(string const & filename)
384 {
385         // Split argument by :
386         string name;
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, "\\"))) {
392                 name += ':';
393                 name += token(tmpname, ':', 0);
394                 tmpname = split(tmpname, ':');
395         }
396 #endif
397         lyxerr.debug() << "Arg is " << filename
398                        << "\nName is " << name
399                        << "\nTemplate is " << tmpname << endl;
400
401         // find a free buffer 
402         Buffer * tmpbuf = bufferlist.newFile(name, tmpname);
403         if (tmpbuf)
404                 lastfiles->newFile(tmpbuf->fileName());
405         return tmpbuf;
406 }
407
408
409 // Insert ascii file (if filename is empty, prompt for one)
410 void InsertAsciiFile(BufferView * bv, string const & f, bool asParagraph)
411 {
412         string fname = f;
413
414         if (!bv->available()) 
415                 return;
416      
417         if (fname.empty()) {
418                 FileDialog fileDlg(bv->owner(), _("Select file to insert"),
419                         (asParagraph) ? LFUN_FILE_INSERT_ASCII_PARA : LFUN_FILE_INSERT_ASCII);
420  
421                 FileDialog::Result result = fileDlg.Select(bv->owner()->buffer()->filepath);
422
423                 if (result.first == FileDialog::Later)
424                         return;
425
426                 fname = result.second;
427
428                 if (fname.empty()) 
429                         return;
430         }
431
432         FileInfo fi(fname);
433
434         if (!fi.readable()) {
435                 WriteFSAlert(_("Error! Specified file is unreadable: "),
436                              MakeDisplayPath(fname, 50));
437                 return;
438         }
439
440         ifstream ifs(fname.c_str());
441         if (!ifs) {
442                 WriteFSAlert(_("Error! Cannot open specified file: "),
443                              MakeDisplayPath(fname, 50));
444                 return;
445         }
446
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...
452         vector<char> tmp;
453         copy(ii, end, back_inserter(tmp));
454         string const tmpstr(tmp.begin(), tmp.end());
455 #else
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:
460         string tmpstr;
461         copy(ii, end, back_inserter(tmpstr));
462 #endif
463         // insert the string
464         bv->hideCursor();
465         
466         // clear the selection
467         bv->beforeChange(bv->text);
468         if (!asParagraph)
469                 bv->text->InsertStringA(bv, tmpstr);
470         else
471                 bv->text->InsertStringB(bv, tmpstr);
472         bv->update(bv->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
473 }
474
475
476 void MenuInsertLabel(BufferView * bv, string const & arg)
477 {
478         string label(arg);
479         ProhibitInput(bv);
480         if (label.empty()) {
481 #ifndef NEW_INSETS
482                 LyXParagraph * par =
483                         bv->text->cursor.par()->FirstPhysicalPar();
484 #else
485                 LyXParagraph * par = bv->text->cursor.par();
486 #endif
487                 LyXLayout const * layout =
488                         &textclasslist.Style(bv->buffer()->params.textclass,
489                                              par->GetLayout());
490
491 #ifndef NEW_INSETS
492                 if (layout->latextype == LATEX_PARAGRAPH && par->previous_) {
493                         LyXParagraph * par2 = par->previous_->FirstPhysicalPar();
494 #else
495                 if (layout->latextype == LATEX_PARAGRAPH && par->previous()) {
496                         LyXParagraph * par2 = par->previous();
497 #endif
498                         LyXLayout const * layout2 =
499                                 &textclasslist.Style(bv->buffer()->params.textclass,
500                                                      par2->GetLayout());
501                         if (layout2->latextype != LATEX_PARAGRAPH) {
502                                 par = par2;
503                                 layout = layout2;
504                         }
505                 }
506                 string text = layout->latexname().substr(0, 3);
507                 if (layout->latexname() == "theorem")
508                         text = "thm"; // Create a correct prefix for prettyref
509 #ifndef NEW_INSETS
510                 if (par->footnoteflag==LyXParagraph::OPEN_FOOTNOTE)
511                         switch (par->footnotekind) {
512                         case LyXParagraph::FIG:
513                         case LyXParagraph::WIDE_FIG:
514                                 text = "fig";
515                                 break;
516                         case LyXParagraph::TAB:
517                         case LyXParagraph::WIDE_TAB:
518                                 text = "tab";
519                                 break;
520                         case LyXParagraph::ALGORITHM:
521                                 text = "alg";
522                                 break;
523                         case LyXParagraph::FOOTNOTE:    
524                         case LyXParagraph::MARGIN:
525                                 break;
526                         }
527 #endif
528                 text += ":";
529                 if (layout->latextype == LATEX_PARAGRAPH ||
530                     lyxrc.label_init_length < 0)
531                         text.erase();
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())
535                                 break;
536                         string head;
537                         par_text = split(par_text, head, ' ');
538                         if (i > 0)
539                                 text += '-'; // Is it legal to use spaces in
540                                              // labels ?
541                         text += head;
542                 }
543
544                 pair<bool, string> result =
545                         askForText(_("Enter new label to insert:"), text);
546                 if (result.first) {
547                         label = frontStrip(strip(result.second));
548                 }
549         }
550         if (!label.empty()) {
551                 InsetCommandParams p( "label", label );
552                 InsetLabel * inset = new InsetLabel( p );
553                 bv->insertInset( inset );
554         }
555         AllowInput(bv);
556 }
557
558
559 void MenuLayoutSave(BufferView * bv)
560 {
561         if (!bv->available())
562                 return;
563
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();
568 }
569
570
571 void Figure()
572 {
573         if (fd_form_figure->form_figure->visible) {
574                 fl_raise_form(fd_form_figure->form_figure);
575         } else {
576                 fl_show_form(fd_form_figure->form_figure,
577                              FL_PLACE_MOUSE | FL_FREE_SIZE, FL_TRANSIENT,
578                              _("Insert Figure"));
579         }
580 }
581
582
583 /* callbacks for form form_figure */
584 extern "C"
585 void FigureApplyCB(FL_OBJECT *, long)
586 {
587         if (!current_view->available())
588                 return;
589
590         Buffer * buffer = current_view->buffer();
591         if (buffer->isReadonly()) // paranoia
592                 return;
593         
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);
600                 return;
601         }
602         
603         current_view->hideCursor();
604         current_view->update(current_view->text, BufferView::SELECT|BufferView::FITCUR);
605         current_view->beforeChange(current_view->text);
606       
607         current_view->text->SetCursorParUndo(current_view->buffer()); 
608         current_view->text->FreezeUndo();
609
610         current_view->text->BreakParagraph(current_view);
611         current_view->update(current_view->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
612
613 #ifndef NEW_INSETS
614         if (current_view->text->cursor.par()->Last()) {
615 #else
616         if (current_view->text->cursor.par()->size()) {
617 #endif
618                 current_view->text->CursorLeft(current_view);
619          
620                 current_view->text->BreakParagraph(current_view);
621                 current_view->update(current_view->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
622         }
623
624         // The standard layout should always be numer 0;
625         current_view->text->SetLayout(current_view, 0);
626
627 #ifndef NEW_INSETS
628         if (current_view->text->cursor.par()->footnoteflag == 
629             LyXParagraph::NO_FOOTNOTE) {
630 #endif
631                 current_view->text->
632                         SetParagraph(current_view, 0, 0,
633                                      0, 0,
634                                      VSpace (0.3 * buffer->params.spacing.getValue(),
635                                              LyXLength::CM),
636                                      VSpace (0.3 *
637                                              buffer->params.spacing.getValue(),
638                                              LyXLength::CM),
639                                      LYX_ALIGN_CENTER, string(), 0);
640 #ifndef NEW_INSETS
641         } else {
642                 current_view->text->SetParagraph(current_view, 0, 0,
643                                                  0, 0,
644                                                  VSpace(VSpace::NONE),
645                                                  VSpace(VSpace::NONE),
646                                                  LYX_ALIGN_CENTER, 
647                                                  string(),
648                                                  0);
649         }
650 #endif
651         
652         current_view->update(current_view->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
653       
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();
661 }
662
663
664 extern "C"
665 void FigureCancelCB(FL_OBJECT *, long)
666 {
667         fl_hide_form(fd_form_figure->form_figure);
668 }
669
670
671 extern "C"
672 void FigureOKCB(FL_OBJECT * ob, long data)
673 {
674         FigureApplyCB(ob, data);
675         FigureCancelCB(ob, data);
676 }
677
678
679 // This function runs "configure" and then rereads lyx.defaults to
680 // reconfigure the automatic settings.
681 void Reconfigure(BufferView * bv)
682 {
683         bv->owner()->getMiniBuffer()->Set(_("Running configure..."));
684
685         // Run configure in user lyx directory
686         Path p(user_lyxdir);
687         Systemcalls one(Systemcalls::System, 
688                         AddName(system_lyxdir, "configure"));
689         p.pop();
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."));
695 }