]> git.lyx.org Git - lyx.git/blob - src/lyx_cb.C
update libtool
[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 <fstream>
14 #include <algorithm>
15 #include <utility> 
16 #include <iostream>
17
18 #include FORMS_H_LOCATION
19
20 #include "lyx_cb.h"
21 #include "lyx_gui_misc.h"
22 #include "lyx_main.h"
23 #include "bufferlist.h"
24 #include "bufferview_funcs.h"
25 #include "lastfiles.h"
26 #include "LyXView.h"
27 #include "lyxrc.h"
28 #include "lyxtext.h"
29 #include "frontends/FileDialog.h"
30 #include "insets/insetlabel.h"
31 #include "support/FileInfo.h"
32 #include "support/filetools.h"
33 #include "support/path.h"
34 #include "support/syscall.h"
35 #include "gettext.h"
36
37 using std::vector;
38 using std::ifstream;
39 using std::copy;
40 using std::endl;
41 using std::ios;
42 using std::back_inserter;
43 using std::istream_iterator;
44 using std::pair;
45 using std::make_pair;
46
47 extern BufferList bufferlist;
48 // this should be static, but I need it in buffer.C
49 bool quitting;  // flag, that we are quitting the program
50 extern bool finished; // all cleanup done just let it run through now.
51
52 /* 
53    This is the inset locking stuff needed for mathed --------------------
54
55    an inset can simple call LockInset in it's edit call and *ONLY* in it's
56    edit call.
57    Inset::Edit() can only be called by the main lyx module.
58
59    Then the inset may modify the menu's and/or iconbars. 
60
61    Unlocking is either done by LyX or the inset itself with a UnlockInset-call
62
63    During the lock, all button and keyboard events will be modified
64    and send to the inset through the following inset-features. Note that
65    Inset::InsetUnlock will be called from inside UnlockInset. It is meant
66    to contain the code for restoring the menus and things like this.
67
68    
69    virtual void InsetButtonPress(int x, int y, int button);
70    virtual void InsetButtonRelease(int x, int y, int button);
71    virtual void InsetKeyPress(XKeyEvent *ev);
72    virtual void InsetMotionNotify(int x, int y, int state);
73    virtual void InsetUnlock();
74
75    If a inset wishes any redraw and/or update it just has to call
76    UpdateInset(this).
77    It's is completly irrelevant, where the inset is. UpdateInset will
78    find it in any paragraph in any buffer. 
79    Of course the_locking_inset and the insets in the current paragraph/buffer
80    are checked first, so no performance problem should occur.
81    
82    Hope that's ok for the beginning, Alejandro,
83    sorry that I needed so much time,
84
85                   Matthias
86    */
87
88 //void UpdateInset(BufferView * bv, Inset * inset, bool mark_dirty = true);
89
90 /* these functions return 1 if an error occured, 
91    otherwise 0 */
92 // Now they work only for updatable insets. [Alejandro 080596]
93 //int LockInset(UpdatableInset * inset);
94 void ToggleLockedInsetCursor(int x, int y, int asc, int desc);
95 //void FitLockedInsetCursor(long x, long y, int asc, int desc);
96 //int UnlockInset(UpdatableInset * inset);
97 //void LockedInsetStoreUndo(Undo::undo_kind kind);
98
99 /* this is for asyncron updating. UpdateInsetUpdateList will be called
100    automatically from LyX. Just insert the Inset into the Updatelist */
101 //void UpdateInsetUpdateList();
102 //void PutInsetIntoInsetUpdateList(Inset * inset);
103
104 //InsetUpdateStruct * InsetUpdateList = 0;
105
106
107 /*
108   -----------------------------------------------------------------------
109  */
110
111
112 void ShowMessage(Buffer const * buf,
113                  string const & msg1,
114                  string const & msg2,
115                  string const & msg3)
116 {
117         if (lyxrc.use_gui) {
118                 string const str = msg1 + ' ' + msg2 + ' ' + msg3;
119                 buf->getUser()->owner()->message(str);
120         } else
121                 lyxerr << msg1 << msg2 << msg3 << endl;
122 }
123
124
125 //
126 // Menu callbacks
127 //
128
129 //
130 // File menu
131 //
132 // should be moved to lyxfunc.C
133 bool MenuWrite(BufferView * bv, Buffer * buffer)
134 {
135         // FIXME: needed ?
136         XFlush(fl_get_display());
137  
138         if (!buffer->save()) {
139                 string const fname = buffer->fileName();
140                 string const s = MakeAbsPath(fname);
141                 if (AskQuestion(_("Save failed. Rename and try again?"),
142                                 MakeDisplayPath(s, 50),
143                                 _("(If not, document is not saved.)"))) {
144                         return WriteAs(bv, buffer);
145                 }
146                 return false;
147         } else
148                 lastfiles->newFile(buffer->fileName());
149         return true;
150 }
151
152
153
154 // should be moved to BufferView.C
155 // Half of this func should be in LyXView, the rest in BufferView.
156 bool WriteAs(BufferView * bv, Buffer * buffer, string const & filename)
157 {
158         string fname = buffer->fileName();
159         string oldname = fname;
160
161         if (filename.empty()) {
162
163                 FileDialog fileDlg(bv->owner(),
164                                    _("Choose a filename to save document as"),
165                         LFUN_WRITEAS,
166                         make_pair(string(_("Documents")),
167                                   string(lyxrc.document_path)),
168                         make_pair(string(_("Templates")),
169                                   string(lyxrc.template_path)));
170
171                 if (!IsLyXFilename(fname))
172                         fname += ".lyx";
173
174                 FileDialog::Result result =
175                         fileDlg.Select(OnlyPath(fname),
176                                        _("*.lyx|LyX Documents (*.lyx)"),
177                                        OnlyFilename(fname));
178
179                 if (result.first == FileDialog::Later)
180                         return false;
181
182                 fname = result.second;
183
184                 if (fname.empty())
185                         return false;
186
187                 // Make sure the absolute filename ends with appropriate suffix
188                 fname = MakeAbsPath(fname);
189                 if (!IsLyXFilename(fname))
190                         fname += ".lyx";
191         } else
192                 fname = filename;
193
194         // Same name as we have already?
195         if (!buffer->isUnnamed() && fname == oldname) {
196                 if (!AskQuestion(_("Same name as document already has:"),
197                                  MakeDisplayPath(fname, 50),
198                                  _("Save anyway?")))
199                         return false;
200                 // Falls through to name change and save
201         } 
202         // No, but do we have another file with this name open?
203         else if (!buffer->isUnnamed() && bufferlist.exists(fname)) {
204                 if (AskQuestion(_("Another document with same name open!"),
205                                 MakeDisplayPath(fname, 50),
206                                 _("Replace with current document?")))
207                         {
208                                 bufferlist.close(bufferlist.getBuffer(fname));
209
210                                 // Ok, change the name of the buffer, but don't save!
211                                 buffer->setFileName(fname);
212                                 buffer->markDirty();
213
214                                 ShowMessage(buffer, _("Document renamed to '"),
215                                                 MakeDisplayPath(fname), _("', but not saved..."));
216                 }
217                 return false;
218         } // Check whether the file exists
219         else {
220                 FileInfo const myfile(fname);
221                 if (myfile.isOK() && !AskQuestion(_("Document already exists:"), 
222                                                   MakeDisplayPath(fname, 50),
223                                                   _("Replace file?")))
224                         return false;
225         }
226
227         // Ok, change the name of the buffer
228         buffer->setFileName(fname);
229         buffer->markDirty();
230         bool unnamed = buffer->isUnnamed();
231         buffer->setUnnamed(false);
232
233         if (!MenuWrite(bv, buffer)) {
234             buffer->setFileName(oldname);
235             buffer->setUnnamed(unnamed);
236             ShowMessage(buffer, _("Document could not be saved!"),
237                         _("Holding the old name."), MakeDisplayPath(oldname));
238             return false;
239         }
240         // now remove the oldname autosave file if existant!
241         removeAutosaveFile(oldname);
242         return true;
243 }
244
245
246 int MenuRunChktex(Buffer * buffer)
247 {
248         int ret;
249
250         if (buffer->isSGML()) {
251                 WriteAlert(_("Chktex does not work with SGML derived documents."));
252                 return 0;
253         } else 
254                 ret = buffer->runChktex();
255    
256         if (ret >= 0) {
257                 string s;
258                 string t;
259                 if (ret == 0) {
260                         s = _("No warnings found.");
261                 } else if (ret == 1) {
262                         s = _("One warning found.");
263                         t = _("Use 'Edit->Go to Error' to find it.");
264                 } else {
265                         s += tostr(ret);
266                         s += _(" warnings found.");
267                         t = _("Use 'Edit->Go to Error' to find them.");
268                 }
269                 WriteAlert(_("Chktex run successfully"), s, t);
270         } else {
271                 WriteAlert(_("Error!"), _("It seems chktex does not work."));
272         }
273         return ret;
274 }
275
276
277 void QuitLyX()
278 {
279         lyxerr.debug() << "Running QuitLyX." << endl;
280
281         if (lyxrc.use_gui) {
282                 if (!bufferlist.QwriteAll())
283                         return;
284
285                 lastfiles->writeFile(lyxrc.lastfiles);
286         }
287
288         // Set a flag that we do quitting from the program,
289         // so no refreshes are necessary.
290         quitting = true;
291
292         // close buffers first
293         bufferlist.closeAll();
294
295         // do any other cleanup procedures now
296         lyxerr.debug() << "Deleting tmp dir " << system_tempdir << endl;
297
298         DestroyLyXTmpDir(system_tempdir);
299
300         finished = true;
301 }
302
303
304
305 void AutoSave(BufferView * bv)
306         // should probably be moved into BufferList (Lgb)
307         // Perfect target for a thread...
308 {
309         if (!bv->available())
310                 return;
311
312         if (bv->buffer()->isBakClean() || bv->buffer()->isReadonly()) {
313                 // We don't save now, but we'll try again later
314                 bv->owner()->resetAutosaveTimer();
315                 return;
316         }
317
318         bv->owner()->message(_("Autosaving current document..."));
319         
320         // create autosave filename
321         string fname =  OnlyPath(bv->buffer()->fileName());
322         fname += "#";
323         fname += OnlyFilename(bv->buffer()->fileName());
324         fname += "#";
325         
326         // tmp_ret will be located (usually) in /tmp
327         // will that be a problem?
328         pid_t const pid = fork(); // If you want to debug the autosave
329         // you should set pid to -1, and comment out the
330         // fork.
331         if (pid == 0 || pid == -1) {
332                 // pid = -1 signifies that lyx was unable
333                 // to fork. But we will do the save
334                 // anyway.
335                 bool failed = false;
336                 
337                 string const tmp_ret = lyx::tempName(string(), "lyxauto");
338                 if (!tmp_ret.empty()) {
339                         bv->buffer()->writeFile(tmp_ret, 1);
340                         // assume successful write of tmp_ret
341                         if (!lyx::rename(tmp_ret, fname)) {
342                                 failed = true;
343                                 // most likely couldn't move between filesystems
344                                 // unless write of tmp_ret failed
345                                 // so remove tmp file (if it exists)
346                                 lyx::unlink(tmp_ret);
347                         }
348                 } else {
349                         failed = true;
350                 }
351                 
352                 if (failed) {
353                         // failed to write/rename tmp_ret so try writing direct
354                         if (!bv->buffer()->writeFile(fname, 1)) {
355                                 // It is dangerous to do this in the child,
356                                 // but safe in the parent, so...
357                                 if (pid == -1)
358                                         bv->owner()->message(_("Autosave Failed!"));
359                         }
360                 }
361                 if (pid == 0) { // we are the child so...
362                         _exit(0);
363                 }
364         }
365         
366         bv->buffer()->markBakClean();
367         bv->owner()->resetAutosaveTimer();
368 }
369
370
371 //
372 // Copyright CHT Software Service GmbH
373 // Uwe C. Schroeder
374 //
375 // create new file with template
376 // SERVERCMD !
377 //
378 Buffer * NewLyxFile(string const & filename)
379 {
380         // Split argument by :
381         string name;
382         string tmpname = split(filename, name, ':');
383 #ifdef __EMX__ // Fix me! lyx_cb.C may not be low level enough to allow this.
384         if (name.length() == 1
385             && isalpha(static_cast<unsigned char>(name[0]))
386             && (prefixIs(tmpname, "/") || prefixIs(tmpname, "\\"))) {
387                 name += ':';
388                 name += token(tmpname, ':', 0);
389                 tmpname = split(tmpname, ':');
390         }
391 #endif
392         lyxerr.debug() << "Arg is " << filename
393                        << "\nName is " << name
394                        << "\nTemplate is " << tmpname << endl;
395
396         // find a free buffer 
397         Buffer * tmpbuf = bufferlist.newFile(name, tmpname);
398         if (tmpbuf)
399                 lastfiles->newFile(tmpbuf->fileName());
400         return tmpbuf;
401 }
402
403
404 // Insert ascii file (if filename is empty, prompt for one)
405 void InsertAsciiFile(BufferView * bv, string const & f, bool asParagraph)
406 {
407         string fname = f;
408
409         if (!bv->available()) 
410                 return;
411      
412         if (fname.empty()) {
413                 FileDialog fileDlg(bv->owner(), _("Select file to insert"),
414                         (asParagraph) ? LFUN_FILE_INSERT_ASCII_PARA : LFUN_FILE_INSERT_ASCII);
415  
416                 FileDialog::Result result = fileDlg.Select(bv->owner()->buffer()->filepath);
417
418                 if (result.first == FileDialog::Later)
419                         return;
420
421                 fname = result.second;
422
423                 if (fname.empty()) 
424                         return;
425         }
426
427         FileInfo fi(fname);
428
429         if (!fi.readable()) {
430                 WriteFSAlert(_("Error! Specified file is unreadable: "),
431                              MakeDisplayPath(fname, 50));
432                 return;
433         }
434
435         ifstream ifs(fname.c_str());
436         if (!ifs) {
437                 WriteFSAlert(_("Error! Cannot open specified file: "),
438                              MakeDisplayPath(fname, 50));
439                 return;
440         }
441
442         ifs.unsetf(ios::skipws);
443         istream_iterator<char> ii(ifs);
444         istream_iterator<char> end;
445 #if !defined(USE_INCLUDED_STRING) && !defined(STD_STRING_IS_GOOD)
446         // We use this until the compilers get better...
447         vector<char> tmp;
448         copy(ii, end, back_inserter(tmp));
449         string const tmpstr(tmp.begin(), tmp.end());
450 #else
451         // This is what we want to use and what we will use once the
452         // compilers get good enough. 
453         //string tmpstr(ii, end); // yet a reason for using std::string
454         // alternate approach to get the file into a string:
455         string tmpstr;
456         copy(ii, end, back_inserter(tmpstr));
457 #endif
458         // insert the string
459         bv->hideCursor();
460         
461         // clear the selection
462         bv->beforeChange(bv->text);
463         if (!asParagraph)
464                 bv->text->InsertStringA(bv, tmpstr);
465         else
466                 bv->text->InsertStringB(bv, tmpstr);
467         bv->update(bv->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
468 }
469
470
471 void MenuInsertLabel(BufferView * bv, string const & arg)
472 {
473         string label(arg);
474         ProhibitInput(bv);
475         if (label.empty()) {
476                 LyXParagraph * par = bv->text->cursor.par();
477                 LyXLayout const * layout =
478                         &textclasslist.Style(bv->buffer()->params.textclass,
479                                              par->GetLayout());
480
481                 if (layout->latextype == LATEX_PARAGRAPH && par->previous()) {
482                         LyXParagraph * par2 = par->previous();
483                         LyXLayout const * layout2 =
484                                 &textclasslist.Style(bv->buffer()->params.textclass,
485                                                      par2->GetLayout());
486                         if (layout2->latextype != LATEX_PARAGRAPH) {
487                                 par = par2;
488                                 layout = layout2;
489                         }
490                 }
491                 string text = layout->latexname().substr(0, 3);
492                 if (layout->latexname() == "theorem")
493                         text = "thm"; // Create a correct prefix for prettyref
494
495                 text += ":";
496                 if (layout->latextype == LATEX_PARAGRAPH ||
497                     lyxrc.label_init_length < 0)
498                         text.erase();
499                 string par_text = par->String(bv->buffer(), false);
500                 for (int i = 0; i < lyxrc.label_init_length; ++i) {
501                         if (par_text.empty())
502                                 break;
503                         string head;
504                         par_text = split(par_text, head, ' ');
505                         if (i > 0)
506                                 text += '-'; // Is it legal to use spaces in
507                                              // labels ?
508                         text += head;
509                 }
510
511                 pair<bool, string> result =
512                         askForText(_("Enter new label to insert:"), text);
513                 if (result.first) {
514                         label = frontStrip(strip(result.second));
515                 }
516         }
517         if (!label.empty()) {
518                 InsetCommandParams p( "label", label );
519                 InsetLabel * inset = new InsetLabel( p );
520                 bv->insertInset( inset );
521         }
522         AllowInput(bv);
523 }
524
525
526 void MenuLayoutSave(BufferView * bv)
527 {
528         if (!bv->available())
529                 return;
530
531         if (AskQuestion(_("Do you want to save the current settings"),
532                         _("for Character, Document, Paper and Quotes"),
533                         _("as default for new documents?")))
534                 bv->buffer()->saveParamsAsDefaults();
535 }
536
537
538 // This function runs "configure" and then rereads lyx.defaults to
539 // reconfigure the automatic settings.
540 void Reconfigure(BufferView * bv)
541 {
542         bv->owner()->message(_("Running configure..."));
543
544         // Run configure in user lyx directory
545         Path p(user_lyxdir);
546         Systemcalls one(Systemcalls::System, 
547                         AddName(system_lyxdir, "configure"));
548         p.pop();
549         bv->owner()->message(_("Reloading configuration..."));
550         lyxrc.read(LibFileSearch(string(), "lyxrc.defaults"));
551         WriteAlert(_("The system has been reconfigured."), 
552                    _("You need to restart LyX to make use of any"),
553                    _("updated document class specifications."));
554 }