]> git.lyx.org Git - lyx.git/blob - src/lyx_cb.C
character2.diff.gz
[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 <iostream>
16
17 #include FORMS_H_LOCATION
18 #include "lyx.h"
19 #include "lyx_main.h"
20 #include "lyx_cb.h"
21 #include "insets/insetlabel.h"
22 #include "insets/figinset.h"
23 #include "lyxfunc.h"
24 #include "minibuffer.h"
25 #include "combox.h"
26 #include "bufferlist.h"
27 #include "filedlg.h"
28 #include "lyx_gui_misc.h"
29 #include "LyXView.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"
36 #include "lyxrc.h"
37 #include "lyxtext.h"
38
39 using std::ifstream;
40 using std::copy;
41 using std::back_inserter;
42 using std::endl;
43 using std::cout;
44 using std::ios;
45 using std::istream_iterator;
46 using std::pair;
47 using std::vector;
48 using std::sort;
49 using std::equal;
50
51 extern BufferList bufferlist;
52 extern void show_symbols_form();
53 extern FD_form_figure * fd_form_figure;
54
55 extern BufferView * current_view; // called too many times in this file...
56
57 extern void DeleteSimpleCutBuffer(); /* for the cleanup when exiting */
58
59 extern void MenuSendto();
60
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.
64
65 char ascii_type; /* for selection notify callbacks */
66
67 bool scrolling = false;
68
69 /* 
70    This is the inset locking stuff needed for mathed --------------------
71
72    an inset can simple call LockInset in it's edit call and *ONLY* in it's
73    edit call.
74    Inset::Edit() can only be called by the main lyx module.
75
76    Then the inset may modify the menu's and/or iconbars. 
77
78    Unlocking is either done by LyX or the inset itself with a UnlockInset-call
79
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.
84
85    
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();
91
92    If a inset wishes any redraw and/or update it just has to call
93    UpdateInset(this).
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.
98    
99    Hope that's ok for the beginning, Alejandro,
100    sorry that I needed so much time,
101
102                   Matthias
103    */
104
105 //void UpdateInset(BufferView * bv, Inset * inset, bool mark_dirty = true);
106
107 /* these functions return 1 if an error occured, 
108    otherwise 0 */
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);
115
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);
120
121 //InsetUpdateStruct * InsetUpdateList = 0;
122
123
124 /*
125   -----------------------------------------------------------------------
126  */
127
128
129 void ShowMessage(Buffer const * buf,
130                  string const & msg1,
131                  string const & msg2,
132                  string const & msg3, int delay)
133 {
134         if (lyxrc.use_gui)
135                 buf->getUser()->owner()->getMiniBuffer()->Set(msg1, msg2,
136                                                               msg3, delay);
137         else
138                 lyxerr << msg1 << msg2 << msg3 << endl;
139 }
140
141
142 //
143 // Menu callbacks
144 //
145
146 //
147 // File menu
148 //
149
150 // should be moved to lyxfunc.C
151 bool MenuWrite(BufferView * bv, Buffer * buffer)
152 {
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);
161                 }
162                 return false;
163         } else {
164                 lastfiles->newFile(buffer->fileName());
165         }
166         return true;
167 }
168
169
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)
173 {
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;
177
178         string fname = buffer->fileName();
179         string oldname = fname;
180         LyXFileDlg fileDlg;
181
182         ProhibitInput(bv);
183         fileDlg.SetButton(0, _("Documents"), lyxrc.document_path);
184         fileDlg.SetButton(1, _("Templates"), lyxrc.template_path);
185
186         if (!IsLyXFilename(fname))
187                 fname += ".lyx";
188
189         fname = fileDlg.Select(_("Enter Filename to Save Document as"), 
190                                OnlyPath(fname),
191                                "*.lyx", 
192                                OnlyFilename(fname));
193
194         AllowInput(bv);
195
196         if (fname.empty())
197                 return false;
198
199         // Make sure the absolute filename ends with appropriate suffix
200         string s = MakeAbsPath(fname);
201         if (!IsLyXFilename(s))
202                 s += ".lyx";
203
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),
208                                  _("Save anyway?")))
209                         return false;
210                 // Falls through to name change and save
211         } 
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?")))
217                         {
218                                 bufferlist.close(bufferlist.getBuffer(s));
219
220                                 // Ok, change the name of the buffer, but don't save!
221                                 buffer->setFileName(s);
222                                 buffer->markDirty();
223
224                                 ShowMessage(buffer, _("Document renamed to '"),
225                                                 MakeDisplayPath(s), _("', but not saved..."));
226                         }
227                 return false;
228         } // Check whether the file exists
229         else {
230                 FileInfo const myfile(s);
231                 if (myfile.isOK() && !AskQuestion(_("Document already exists:"), 
232                                                   MakeDisplayPath(s, 50),
233                                                   _("Replace file?")))
234                         return false;
235         }
236
237         // Ok, change the name of the buffer
238         buffer->setFileName(s);
239         buffer->markDirty();
240         bool unnamed = buffer->isUnnamed();
241         buffer->setUnnamed(false);
242         // And save
243         // Small bug: If the save fails, we have irreversible changed the name
244         // of the document.
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));
251             return false;
252         }
253         // now remove the oldname autosave file if existant!
254         removeAutosaveFile(oldname);
255         return true;
256 }
257
258
259 int MenuRunChktex(Buffer * buffer)
260 {
261         int ret;
262
263         if (buffer->isSGML()) {
264                 WriteAlert(_("Chktex does not work with SGML derived documents."));
265                 return 0;
266         } else 
267                 ret = buffer->runChktex();
268    
269         if (ret >= 0) {
270                 string s;
271                 string t;
272                 if (ret == 0) {
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.");
277                 } else {
278                         s += tostr(ret);
279                         s += _(" warnings found.");
280                         t = _("Use 'Edit->Go to Error' to find them.");
281                 }
282                 WriteAlert(_("Chktex run successfully"), s, t);
283         } else {
284                 WriteAlert(_("Error!"), _("It seems chktex does not work."));
285         }
286         return ret;
287 }
288
289
290 void QuitLyX()
291 {
292         lyxerr.debug() << "Running QuitLyX." << endl;
293
294         if (lyxrc.use_gui) {
295                 if (!bufferlist.QwriteAll())
296                         return;
297
298                 lastfiles->writeFile(lyxrc.lastfiles);
299         }
300
301         // Set a flag that we do quitting from the program,
302         // so no refreshes are necessary.
303         quitting = true;
304
305         // close buffers first
306         bufferlist.closeAll();
307
308         // do any other cleanup procedures now
309         lyxerr.debug() << "Deleting tmp dir " << system_tempdir << endl;
310
311         DestroyLyXTmpDir(system_tempdir);
312
313         finished = true;
314 }
315
316
317
318 void AutoSave(BufferView * bv)
319         // should probably be moved into BufferList (Lgb)
320         // Perfect target for a thread...
321 {
322         if (!bv->available())
323                 return;
324
325         if (bv->buffer()->isBakClean() || bv->buffer()->isReadonly()) {
326                 // We don't save now, but we'll try again later
327                 bv->owner()->resetAutosaveTimer();
328                 return;
329         }
330
331         bv->owner()->getMiniBuffer()->Set(_("Autosaving current document..."));
332         
333         // create autosave filename
334         string fname =  OnlyPath(bv->buffer()->fileName());
335         fname += "#";
336         fname += OnlyFilename(bv->buffer()->fileName());
337         fname += "#";
338         
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
343         // fork.
344         if (pid == 0 || pid == -1) {
345                 // pid = -1 signifies that lyx was unable
346                 // to fork. But we will do the save
347                 // anyway.
348                 bool failed = false;
349                 
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)) {
355                                 failed = true;
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);
360                         }
361                 } else {
362                         failed = true;
363                 }
364                 
365                 if (failed) {
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...
370                                 if (pid == -1)
371                                         bv->owner()->getMiniBuffer()->Set(_("Autosave Failed!"));
372                         }
373                 }
374                 if (pid == 0) { // we are the child so...
375                         _exit(0);
376                 }
377         }
378         
379         bv->buffer()->markBakClean();
380         bv->owner()->resetAutosaveTimer();
381 }
382
383
384 //
385 // Copyright CHT Software Service GmbH
386 // Uwe C. Schroeder
387 //
388 // create new file with template
389 // SERVERCMD !
390 //
391 Buffer * NewLyxFile(string const & filename)
392 {
393         // Split argument by :
394         string name;
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, "\\"))) {
400                 name += ':';
401                 name += token(tmpname, ':', 0);
402                 tmpname = split(tmpname, ':');
403         }
404 #endif
405         lyxerr.debug() << "Arg is " << filename
406                        << "\nName is " << name
407                        << "\nTemplate is " << tmpname << endl;
408
409         // find a free buffer 
410         Buffer * tmpbuf = bufferlist.newFile(name, tmpname);
411         if (tmpbuf)
412                 lastfiles->newFile(tmpbuf->fileName());
413         return tmpbuf;
414 }
415
416
417 // Insert ascii file (if filename is empty, prompt for one)
418 void InsertAsciiFile(BufferView * bv, string const & f, bool asParagraph)
419 {
420         string fname = f;
421         LyXFileDlg fileDlg;
422  
423         if (!bv->available()) return;
424      
425         if (fname.empty()) {
426                 ProhibitInput(bv);
427                 fname = fileDlg.Select(_("File to Insert"), 
428                                        bv->owner()->buffer()->filepath,
429                                        "*");
430                 AllowInput(bv);
431                 if (fname.empty()) return;
432         }
433
434         FileInfo fi(fname);
435
436         if (!fi.readable()) {
437                 WriteFSAlert(_("Error! Specified file is unreadable: "),
438                              MakeDisplayPath(fname, 50));
439                 return;
440         }
441
442         ifstream ifs(fname.c_str());
443         if (!ifs) {
444                 WriteFSAlert(_("Error! Cannot open specified file: "),
445                              MakeDisplayPath(fname, 50));
446                 return;
447         }
448
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...
454         vector<char> tmp;
455         copy(ii, end, back_inserter(tmp));
456         string const tmpstr(tmp.begin(), tmp.end());
457 #else
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:
462         string tmpstr;
463         copy(ii, end, back_inserter(tmpstr));
464 #endif
465         // insert the string
466         bv->hideCursor();
467         
468         // clear the selection
469         bv->beforeChange(bv->text);
470         if (!asParagraph)
471                 bv->text->InsertStringA(bv, tmpstr);
472         else
473                 bv->text->InsertStringB(bv, tmpstr);
474         bv->update(bv->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
475 }
476
477
478 void MenuInsertLabel(BufferView * bv, string const & arg)
479 {
480         string label(arg);
481         ProhibitInput(bv);
482         if (label.empty()) {
483 #ifndef NEW_INSETS
484                 LyXParagraph * par =
485                         bv->text->cursor.par()->FirstPhysicalPar();
486 #else
487                 LyXParagraph * par = bv->text->cursor.par();
488 #endif
489                 LyXLayout const * layout =
490                         &textclasslist.Style(bv->buffer()->params.textclass,
491                                              par->GetLayout());
492
493                 if (layout->latextype == LATEX_PARAGRAPH && par->previous) {
494 #ifndef NEW_INSETS
495                         LyXParagraph * par2 = par->previous->FirstPhysicalPar();
496 #else
497                         LyXParagraph * par2 = par->previous;
498 #endif
499                         LyXLayout const * layout2 =
500                                 &textclasslist.Style(bv->buffer()->params.textclass,
501                                                      par2->GetLayout());
502                         if (layout2->latextype != LATEX_PARAGRAPH) {
503                                 par = par2;
504                                 layout = layout2;
505                         }
506                 }
507                 string text = layout->latexname().substr(0, 3);
508                 if (layout->latexname() == "theorem")
509                         text = "thm"; // Create a correct prefix for prettyref
510 #ifndef NEW_INSETS
511                 if (par->footnoteflag==LyXParagraph::OPEN_FOOTNOTE)
512                         switch (par->footnotekind) {
513                         case LyXParagraph::FIG:
514                         case LyXParagraph::WIDE_FIG:
515                                 text = "fig";
516                                 break;
517                         case LyXParagraph::TAB:
518                         case LyXParagraph::WIDE_TAB:
519                                 text = "tab";
520                                 break;
521                         case LyXParagraph::ALGORITHM:
522                                 text = "alg";
523                                 break;
524                         case LyXParagraph::FOOTNOTE:    
525                         case LyXParagraph::MARGIN:
526                                 break;
527                         }
528 #endif
529                 text += ":";
530                 if (layout->latextype == LATEX_PARAGRAPH ||
531                     lyxrc.label_init_length < 0)
532                         text.erase();
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())
536                                 break;
537                         string head;
538                         par_text = split(par_text, head, ' ');
539                         if (i > 0)
540                                 text += '-'; // Is it legal to use spaces in
541                                              // labels ?
542                         text += head;
543                 }
544
545                 pair<bool, string> result =
546                         askForText(_("Enter new label to insert:"), text);
547                 if (result.first) {
548                         label = frontStrip(strip(result.second));
549                 }
550         }
551         if (!label.empty()) {
552                 InsetCommandParams p( "label", label );
553                 InsetLabel * inset = new InsetLabel( p );
554                 bv->insertInset( inset );
555         }
556         AllowInput(bv);
557 }
558
559
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 *)
563 {
564         string const tmp = tostr(sel);
565         current_view->owner()->getLyXFunc()->Dispatch(LFUN_LAYOUTNO,
566                                                       tmp);
567 }
568
569
570 void MenuLayoutSave(BufferView * bv)
571 {
572         if (!bv->available())
573                 return;
574
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();
579 }
580
581
582 void Figure()
583 {
584         if (fd_form_figure->form_figure->visible) {
585                 fl_raise_form(fd_form_figure->form_figure);
586         } else {
587                 fl_show_form(fd_form_figure->form_figure,
588                              FL_PLACE_MOUSE | FL_FREE_SIZE, FL_TRANSIENT,
589                              _("Insert Figure"));
590         }
591 }
592
593
594 /* callbacks for form form_figure */
595 extern "C"
596 void FigureApplyCB(FL_OBJECT *, long)
597 {
598         if (!current_view->available())
599                 return;
600
601         Buffer * buffer = current_view->buffer();
602         if (buffer->isReadonly()) // paranoia
603                 return;
604         
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);
611                 return;
612         }
613         
614         current_view->hideCursor();
615         current_view->update(current_view->text, BufferView::SELECT|BufferView::FITCUR);
616         current_view->beforeChange(current_view->text);
617       
618         current_view->text->SetCursorParUndo(current_view->buffer()); 
619         current_view->text->FreezeUndo();
620
621         current_view->text->BreakParagraph(current_view);
622         current_view->update(current_view->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
623       
624         if (current_view->text->cursor.par()->Last()) {
625                 current_view->text->CursorLeft(current_view);
626          
627                 current_view->text->BreakParagraph(current_view);
628                 current_view->update(current_view->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
629         }
630
631         // The standard layout should always be numer 0;
632         current_view->text->SetLayout(current_view, 0);
633
634 #ifndef NEW_INSETS
635         if (current_view->text->cursor.par()->footnoteflag == 
636             LyXParagraph::NO_FOOTNOTE) {
637 #endif
638                 current_view->text->
639                         SetParagraph(current_view, 0, 0,
640                                      0, 0,
641                                      VSpace (0.3 * buffer->params.spacing.getValue(),
642                                              LyXLength::CM),
643                                      VSpace (0.3 *
644                                              buffer->params.spacing.getValue(),
645                                              LyXLength::CM),
646                                      LYX_ALIGN_CENTER, string(), 0);
647 #ifndef NEW_INSETS
648         } else {
649                 current_view->text->SetParagraph(current_view, 0, 0,
650                                                  0, 0,
651                                                  VSpace(VSpace::NONE),
652                                                  VSpace(VSpace::NONE),
653                                                  LYX_ALIGN_CENTER, 
654                                                  string(),
655                                                  0);
656         }
657 #endif
658         
659         current_view->update(current_view->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
660       
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();
668 }
669
670
671 extern "C" void FigureCancelCB(FL_OBJECT *, long)
672 {
673         fl_hide_form(fd_form_figure->form_figure);
674 }
675
676
677 extern "C" void FigureOKCB(FL_OBJECT * ob, long data)
678 {
679         FigureApplyCB(ob, data);
680         FigureCancelCB(ob, data);
681 }
682
683
684 // This function runs "configure" and then rereads lyx.defaults to
685 // reconfigure the automatic settings.
686 void Reconfigure(BufferView * bv)
687 {
688         bv->owner()->getMiniBuffer()->Set(_("Running configure..."));
689
690         // Run configure in user lyx directory
691         Path p(user_lyxdir);
692         Systemcalls one(Systemcalls::System, 
693                         AddName(system_lyxdir, "configure"));
694         p.pop();
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."));
700 }