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