]> git.lyx.org Git - lyx.git/blob - src/lyx_cb.C
John's Layout Tabular UI improvements and Martins fixes to clearing the
[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                 string const fname = buffer->fileName();
147                 string const s = MakeAbsPath(fname);
148                 if (Alert::askQuestion(_("Save failed. Rename and try again?"),
149                                 MakeDisplayPath(s, 50),
150                                 _("(If not, document is not saved.)"))) {
151                         return WriteAs(bv, buffer);
152                 }
153                 return false;
154         } else
155                 lastfiles->newFile(buffer->fileName());
156         return true;
157 }
158
159
160
161 // should be moved to BufferView.C
162 // Half of this func should be in LyXView, the rest in BufferView.
163 bool WriteAs(BufferView * bv, Buffer * buffer, string const & filename)
164 {
165         string fname = buffer->fileName();
166         string const oldname = fname;
167
168         if (filename.empty()) {
169
170                 FileDialog fileDlg(bv->owner(),
171                                    _("Choose a filename to save document as"),
172                         LFUN_WRITEAS,
173                         make_pair(string(_("Documents")),
174                                   string(lyxrc.document_path)),
175                         make_pair(string(_("Templates")),
176                                   string(lyxrc.template_path)));
177
178                 if (!IsLyXFilename(fname))
179                         fname += ".lyx";
180
181                 FileDialog::Result result =
182                         fileDlg.Select(OnlyPath(fname),
183                                        _("*.lyx|LyX Documents (*.lyx)"),
184                                        OnlyFilename(fname));
185
186                 if (result.first == FileDialog::Later)
187                         return false;
188
189                 fname = result.second;
190
191                 if (fname.empty())
192                         return false;
193
194                 // Make sure the absolute filename ends with appropriate suffix
195                 fname = MakeAbsPath(fname);
196                 if (!IsLyXFilename(fname))
197                         fname += ".lyx";
198         } else
199                 fname = filename;
200
201         // Same name as we have already?
202         if (!buffer->isUnnamed() && fname == oldname) {
203                 if (!Alert::askQuestion(_("Same name as document already has:"),
204                                  MakeDisplayPath(fname, 50),
205                                  _("Save anyway?")))
206                         return false;
207                 // Falls through to name change and save
208         } 
209         // No, but do we have another file with this name open?
210         else if (!buffer->isUnnamed() && bufferlist.exists(fname)) {
211                 if (Alert::askQuestion(_("Another document with same name open!"),
212                                 MakeDisplayPath(fname, 50),
213                                 _("Replace with current document?")))
214                         {
215                                 bufferlist.close(bufferlist.getBuffer(fname));
216
217                                 // Ok, change the name of the buffer, but don't save!
218                                 buffer->setFileName(fname);
219                                 buffer->markDirty();
220
221                                 ShowMessage(buffer, _("Document renamed to '"),
222                                                 MakeDisplayPath(fname), _("', but not saved..."));
223                 }
224                 return false;
225         } // Check whether the file exists
226         else {
227                 FileInfo const myfile(fname);
228                 if (myfile.isOK() && !Alert::askQuestion(_("Document already exists:"), 
229                                                   MakeDisplayPath(fname, 50),
230                                                   _("Replace file?")))
231                         return false;
232         }
233
234         // Ok, change the name of the buffer
235         buffer->setFileName(fname);
236         buffer->markDirty();
237         bool unnamed = buffer->isUnnamed();
238         buffer->setUnnamed(false);
239
240         if (!MenuWrite(bv, buffer)) {
241             buffer->setFileName(oldname);
242             buffer->setUnnamed(unnamed);
243             ShowMessage(buffer, _("Document could not be saved!"),
244                         _("Holding the old name."), MakeDisplayPath(oldname));
245             return false;
246         }
247         // now remove the oldname autosave file if existant!
248         removeAutosaveFile(oldname);
249         return true;
250 }
251
252
253 int MenuRunChktex(Buffer * buffer)
254 {
255         int ret;
256
257         if (buffer->isSGML()) {
258                 Alert::alert(_("Chktex does not work with SGML derived documents."));
259                 return 0;
260         } else 
261                 ret = buffer->runChktex();
262    
263         if (ret >= 0) {
264                 string s;
265                 string t;
266                 if (ret == 0) {
267                         s = _("No warnings found.");
268                 } else if (ret == 1) {
269                         s = _("One warning found.");
270                         t = _("Use `Navigate->Error' to find it.");
271                 } else {
272                         s += tostr(ret);
273                         s += _(" warnings found.");
274                         t = _("Use `Navigate->Error' to find them.");
275                 }
276                 Alert::alert(_("Chktex run successfully"), s, t);
277         } else {
278                 Alert::alert(_("Error!"), _("It seems chktex does not work."));
279         }
280         return ret;
281 }
282
283
284 void QuitLyX()
285 {
286         lyxerr[Debug::INFO] << "Running QuitLyX." << endl;
287
288         if (lyxrc.use_gui) {
289                 if (!bufferlist.qwriteAll())
290                         return;
291
292                 lastfiles->writeFile(lyxrc.lastfiles);
293         }
294
295         // Set a flag that we do quitting from the program,
296         // so no refreshes are necessary.
297         quitting = true;
298
299         // close buffers first
300         bufferlist.closeAll();
301
302         // do any other cleanup procedures now
303         lyxerr[Debug::INFO] << "Deleting tmp dir " << system_tempdir << endl;
304
305         DestroyLyXTmpDir(system_tempdir);
306
307         finished = true;
308 }
309
310
311
312 void AutoSave(BufferView * bv)
313         // should probably be moved into BufferList (Lgb)
314         // Perfect target for a thread...
315 {
316         if (!bv->available())
317                 return;
318
319         if (bv->buffer()->isBakClean() || bv->buffer()->isReadonly()) {
320                 // We don't save now, but we'll try again later
321                 bv->owner()->resetAutosaveTimer();
322                 return;
323         }
324
325         bv->owner()->message(_("Autosaving current document..."));
326         
327         // create autosave filename
328         string fname =  OnlyPath(bv->buffer()->fileName());
329         fname += "#";
330         fname += OnlyFilename(bv->buffer()->fileName());
331         fname += "#";
332         
333         // tmp_ret will be located (usually) in /tmp
334         // will that be a problem?
335         pid_t const pid = fork(); // If you want to debug the autosave
336         // you should set pid to -1, and comment out the
337         // fork.
338         if (pid == 0 || pid == -1) {
339                 // pid = -1 signifies that lyx was unable
340                 // to fork. But we will do the save
341                 // anyway.
342                 bool failed = false;
343                 
344                 string const tmp_ret = lyx::tempName(string(), "lyxauto");
345                 if (!tmp_ret.empty()) {
346                         bv->buffer()->writeFile(tmp_ret, 1);
347                         // assume successful write of tmp_ret
348                         if (!lyx::rename(tmp_ret, fname)) {
349                                 failed = true;
350                                 // most likely couldn't move between filesystems
351                                 // unless write of tmp_ret failed
352                                 // so remove tmp file (if it exists)
353                                 lyx::unlink(tmp_ret);
354                         }
355                 } else {
356                         failed = true;
357                 }
358                 
359                 if (failed) {
360                         // failed to write/rename tmp_ret so try writing direct
361                         if (!bv->buffer()->writeFile(fname, 1)) {
362                                 // It is dangerous to do this in the child,
363                                 // but safe in the parent, so...
364                                 if (pid == -1)
365                                         bv->owner()->message(_("Autosave failed!"));
366                         }
367                 }
368                 if (pid == 0) { // we are the child so...
369                         _exit(0);
370                 }
371         }
372         
373         bv->buffer()->markBakClean();
374         bv->owner()->resetAutosaveTimer();
375 }
376
377
378 //
379 // Copyright CHT Software Service GmbH
380 // Uwe C. Schroeder
381 //
382 // create new file with template
383 // SERVERCMD !
384 //
385 Buffer * NewLyxFile(string const & filename)
386 {
387         // Split argument by :
388         string name;
389         string tmpname = split(filename, name, ':');
390 #ifdef __EMX__ // Fix me! lyx_cb.C may not be low level enough to allow this.
391         if (name.length() == 1
392             && isalpha(static_cast<unsigned char>(name[0]))
393             && (prefixIs(tmpname, "/") || prefixIs(tmpname, "\\"))) {
394                 name += ':';
395                 name += token(tmpname, ':', 0);
396                 tmpname = split(tmpname, ':');
397         }
398 #endif
399         lyxerr[Debug::INFO] << "Arg is " << filename
400                             << "\nName is " << name
401                             << "\nTemplate is " << tmpname << endl;
402
403         // find a free buffer 
404         Buffer * tmpbuf = bufferlist.newFile(name, tmpname);
405         if (tmpbuf)
406                 lastfiles->newFile(tmpbuf->fileName());
407         return tmpbuf;
408 }
409
410
411 // Insert ascii file (if filename is empty, prompt for one)
412 void InsertAsciiFile(BufferView * bv, string const & f, bool asParagraph)
413 {
414         string fname = f;
415
416         if (!bv->available()) 
417                 return;
418      
419         if (fname.empty()) {
420                 FileDialog fileDlg(bv->owner(), _("Select file to insert"),
421                         (asParagraph) ? LFUN_FILE_INSERT_ASCII_PARA : LFUN_FILE_INSERT_ASCII);
422  
423                 FileDialog::Result result = fileDlg.Select(bv->owner()->buffer()->filepath);
424
425                 if (result.first == FileDialog::Later)
426                         return;
427
428                 fname = result.second;
429
430                 if (fname.empty()) 
431                         return;
432         }
433
434         FileInfo fi(fname);
435
436         if (!fi.readable()) {
437                 Alert::err_alert(_("Error! Specified file is unreadable: "),
438                              MakeDisplayPath(fname, 50));
439                 return;
440         }
441
442         ifstream ifs(fname.c_str());
443         if (!ifs) {
444                 Alert::err_alert(_("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->insertStringAsLines(bv, tmpstr);
472         else
473                 bv->text->insertStringAsParagraphs(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         bv->owner()->prohibitInput();
482         if (label.empty()) {
483                 Paragraph * par = bv->getLyXText()->cursor.par();
484                 LyXLayout const * layout =
485                         &textclasslist.Style(bv->buffer()->params.textclass,
486                                              par->getLayout());
487
488                 if (layout->latextype == LATEX_PARAGRAPH && par->previous()) {
489                         Paragraph * par2 = par->previous();
490                         LyXLayout const * layout2 =
491                                 &textclasslist.Style(bv->buffer()->params.textclass,
492                                                      par2->getLayout());
493                         if (layout2->latextype != LATEX_PARAGRAPH) {
494                                 par = par2;
495                                 layout = layout2;
496                         }
497                 }
498                 string text = layout->latexname().substr(0, 3);
499                 if (layout->latexname() == "theorem")
500                         text = "thm"; // Create a correct prefix for prettyref
501
502                 text += ":";
503                 if (layout->latextype == LATEX_PARAGRAPH ||
504                     lyxrc.label_init_length < 0)
505                         text.erase();
506                 string par_text = par->asString(bv->buffer(), false);
507                 for (int i = 0; i < lyxrc.label_init_length; ++i) {
508                         if (par_text.empty())
509                                 break;
510                         string head;
511                         par_text = split(par_text, head, ' ');
512                         if (i > 0)
513                                 text += '-'; // Is it legal to use spaces in
514                                              // labels ?
515                         text += head;
516                 }
517
518                 pair<bool, string> result =
519                         Alert::askForText(_("Enter new label to insert:"), text);
520                 if (result.first) {
521                         label = frontStrip(strip(result.second));
522                 }
523         }
524         if (!label.empty()) {
525                 InsetCommandParams p( "label", label );
526                 InsetLabel * inset = new InsetLabel( p );
527                 bv->insertInset( inset );
528         }
529         bv->owner()->allowInput();
530 }
531
532
533 // This function runs "configure" and then rereads lyx.defaults to
534 // reconfigure the automatic settings.
535 void Reconfigure(BufferView * bv)
536 {
537         bv->owner()->message(_("Running configure..."));
538
539         // Run configure in user lyx directory
540         Path p(user_lyxdir);
541         Systemcalls one(Systemcalls::System, 
542                         AddName(system_lyxdir, "configure"));
543         p.pop();
544         bv->owner()->message(_("Reloading configuration..."));
545         lyxrc.read(LibFileSearch(string(), "lyxrc.defaults"));
546         Alert::alert(_("The system has been reconfigured."), 
547                    _("You need to restart LyX to make use of any"),
548                    _("updated document class specifications."));
549 }