]> git.lyx.org Git - lyx.git/blob - src/lyx_cb.C
fix starting up which binary is really a symlink; make sure insetinclude file browser...
[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-2001 The LyX Team.
8  *
9  * ====================================================== */
10
11 #include <config.h>
12
13 #include "lyx_cb.h"
14 #include "lyx_main.h"
15 #include "buffer.h"
16 #include "bufferlist.h"
17 #include "bufferview_funcs.h"
18 #include "debug.h"
19 #include "lastfiles.h"
20 #include "LyXView.h"
21 #include "lyxrc.h"
22 #include "lyxtext.h"
23 #include "gettext.h"
24 #include "BufferView.h"
25 #include "lyxtextclasslist.h"
26
27 #include "insets/insetlabel.h"
28
29 #include "frontends/Alert.h"
30 #include "frontends/FileDialog.h"
31 #include "frontends/GUIRunTime.h"
32
33 #include "support/FileInfo.h"
34 #include "support/filetools.h"
35 #include "support/path.h"
36 #include "support/syscall.h"
37 #include "support/lstrings.h"
38
39 #include <fstream>
40 #include <algorithm>
41 #include <utility> 
42 #include <iostream>
43
44 using std::vector;
45 using std::ifstream;
46 using std::copy;
47 using std::endl;
48 using std::ios;
49 using std::back_inserter;
50 using std::istream_iterator;
51 using std::pair;
52 using std::make_pair;
53
54 extern BufferList bufferlist;
55 // this should be static, but I need it in buffer.C
56 bool quitting;  // flag, that we are quitting the program
57 extern bool finished; // all cleanup done just let it run through now.
58
59 /* 
60    This is the inset locking stuff needed for mathed --------------------
61
62    an inset can simple call LockInset in it's edit call and *ONLY* in it's
63    edit call.
64    Inset::Edit() can only be called by the main lyx module.
65
66    Then the inset may modify the menu's and/or iconbars. 
67
68    Unlocking is either done by LyX or the inset itself with a UnlockInset-call
69
70    During the lock, all button and keyboard events will be modified
71    and send to the inset through the following inset-features. Note that
72    Inset::insetUnlock will be called from inside UnlockInset. It is meant
73    to contain the code for restoring the menus and things like this.
74
75    
76    virtual void insetButtonPress(int x, int y, int button);
77    virtual void insetButtonRelease(int x, int y, int button);
78    virtual void insetKeyPress(XKeyEvent *ev);
79    virtual void insetMotionNotify(int x, int y, int state);
80    virtual void insetUnlock();
81
82    If a inset wishes any redraw and/or update it just has to call
83    UpdateInset(this).
84    It's is completly irrelevant, where the inset is. UpdateInset will
85    find it in any paragraph in any buffer. 
86    Of course the_locking_inset and the insets in the current paragraph/buffer
87    are checked first, so no performance problem should occur.
88    
89    Hope that's ok for the beginning, Alejandro,
90    sorry that I needed so much time,
91
92                   Matthias
93    */
94
95 //void UpdateInset(BufferView * bv, Inset * inset, bool mark_dirty = true);
96
97 /* these functions return 1 if an error occured, 
98    otherwise 0 */
99 // Now they work only for updatable insets. [Alejandro 080596]
100 //int LockInset(UpdatableInset * inset);
101 void ToggleLockedInsetCursor(int x, int y, int asc, int desc);
102 //void FitLockedInsetCursor(long x, long y, int asc, int desc);
103 //int UnlockInset(UpdatableInset * inset);
104 //void LockedInsetStoreUndo(Undo::undo_kind kind);
105
106 /* this is for asyncron updating. UpdateInsetUpdateList will be called
107    automatically from LyX. Just insert the Inset into the Updatelist */
108 //void UpdateInsetUpdateList();
109 //void PutInsetIntoInsetUpdateList(Inset * inset);
110
111 //InsetUpdateStruct * InsetUpdateList = 0;
112
113
114 /*
115   -----------------------------------------------------------------------
116  */
117
118
119 void ShowMessage(Buffer const * buf,
120                  string const & msg1,
121                  string const & msg2,
122                  string const & msg3)
123 {
124         if (lyxrc.use_gui) {
125                 string const str = msg1 + ' ' + msg2 + ' ' + msg3;
126                 buf->getUser()->owner()->message(str);
127         } else
128                 lyxerr << msg1 << msg2 << msg3 << endl;
129 }
130
131
132 //
133 // Menu callbacks
134 //
135
136 //
137 // File menu
138 //
139 // should be moved to lyxfunc.C
140 bool MenuWrite(BufferView * bv, Buffer * buffer)
141 {
142         // FIXME: needed ?
143         XFlush(GUIRunTime::x11Display());
144  
145         if (!buffer->save()) {
146                 if (Alert::askQuestion(_("Save failed. Rename and try again?"),
147                                 MakeDisplayPath(buffer->fileName(), 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 const 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|#o#O")),
172                                   string(lyxrc.document_path)),
173                         make_pair(string(_("Templates|#T#t")),
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 (!Alert::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 (Alert::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() && !Alert::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                 Alert::alert(_("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 `Navigate->Error' to find it.");
269                 } else {
270                         s += tostr(ret);
271                         s += _(" warnings found.");
272                         t = _("Use `Navigate->Error' to find them.");
273                 }
274                 Alert::alert(_("Chktex run successfully"), s, t);
275         } else {
276                 Alert::alert(_("Error!"), _("It seems chktex does not work."));
277         }
278         return ret;
279 }
280
281
282 void QuitLyX()
283 {
284         lyxerr[Debug::INFO] << "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::INFO] << "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()->message(_("Autosaving current document..."));
324         
325         // create autosave filename
326         string fname =  bv->buffer()->filePath();
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()->message(_("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::INFO] << "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                 Alert::err_alert(_("Error! Specified file is unreadable: "),
436                              MakeDisplayPath(fname, 50));
437                 return;
438         }
439
440         ifstream ifs(fname.c_str());
441         if (!ifs) {
442                 Alert::err_alert(_("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->insertStringAsLines(bv, tmpstr);
470         else
471                 bv->text->insertStringAsParagraphs(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         bv->owner()->prohibitInput();
480         if (label.empty()) {
481                 Paragraph * par = bv->getLyXText()->cursor.par();
482                 LyXLayout const * layout =
483                         &textclasslist.Style(bv->buffer()->params.textclass,
484                                              par->getLayout());
485
486                 if (layout->latextype == LATEX_PARAGRAPH && par->previous()) {
487                         Paragraph * par2 = par->previous();
488                         LyXLayout const * layout2 =
489                                 &textclasslist.Style(bv->buffer()->params.textclass,
490                                                      par2->getLayout());
491                         if (layout2->latextype != LATEX_PARAGRAPH) {
492                                 par = par2;
493                                 layout = layout2;
494                         }
495                 }
496                 string text = layout->latexname().substr(0, 3);
497                 if (layout->latexname() == "theorem")
498                         text = "thm"; // Create a correct prefix for prettyref
499
500                 text += ":";
501                 if (layout->latextype == LATEX_PARAGRAPH ||
502                     lyxrc.label_init_length < 0)
503                         text.erase();
504                 string par_text = par->asString(bv->buffer(), false);
505                 for (int i = 0; i < lyxrc.label_init_length; ++i) {
506                         if (par_text.empty())
507                                 break;
508                         string head;
509                         par_text = split(par_text, head, ' ');
510                         if (i > 0)
511                                 text += '-'; // Is it legal to use spaces in
512                                              // labels ?
513                         text += head;
514                 }
515
516                 pair<bool, string> result =
517                         Alert::askForText(_("Enter new label to insert:"), text);
518                 if (result.first) {
519                         label = frontStrip(strip(result.second));
520                 }
521         }
522         if (!label.empty()) {
523                 InsetCommandParams p( "label", label );
524                 InsetLabel * inset = new InsetLabel( p );
525                 bv->insertInset( inset );
526         }
527         bv->owner()->allowInput();
528 }
529
530
531 // This function runs "configure" and then rereads lyx.defaults to
532 // reconfigure the automatic settings.
533 void Reconfigure(BufferView * bv)
534 {
535         bv->owner()->message(_("Running configure..."));
536
537         // Run configure in user lyx directory
538         Path p(user_lyxdir);
539         Systemcalls one(Systemcalls::System, 
540                         AddName(system_lyxdir, "configure"));
541         p.pop();
542         bv->owner()->message(_("Reloading configuration..."));
543         lyxrc.read(LibFileSearch(string(), "lyxrc.defaults"));
544         Alert::alert(_("The system has been reconfigured."), 
545                    _("You need to restart LyX to make use of any"),
546                    _("updated document class specifications."));
547 }