]> git.lyx.org Git - lyx.git/blob - src/lyx_cb.C
layout as string
[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/systemcall.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 void ShowMessage(Buffer const * buf,
61                  string const & msg1,
62                  string const & msg2,
63                  string const & msg3)
64 {
65         if (lyxrc.use_gui
66             && buf && buf->getUser() && buf->getUser()->owner()) {
67                         string const str = msg1 + ' ' + msg2 + ' ' + msg3;
68                         buf->getUser()->owner()->message(str);
69         } else
70                 lyxerr << msg1 << msg2 << msg3 << endl;
71 }
72
73
74 //
75 // Menu callbacks
76 //
77
78 //
79 // File menu
80 //
81 // should be moved to lyxfunc.C
82 bool MenuWrite(BufferView * bv, Buffer * buffer)
83 {
84         // FIXME: needed ?
85         XFlush(GUIRunTime::x11Display());
86  
87         if (!buffer->save()) {
88                 if (Alert::askQuestion(_("Save failed. Rename and try again?"),
89                                 MakeDisplayPath(buffer->fileName(), 50),
90                                 _("(If not, document is not saved.)"))) {
91                         return WriteAs(bv, buffer);
92                 }
93                 return false;
94         } else
95                 lastfiles->newFile(buffer->fileName());
96         return true;
97 }
98
99
100
101 // should be moved to BufferView.C
102 // Half of this func should be in LyXView, the rest in BufferView.
103 bool WriteAs(BufferView * bv, Buffer * buffer, string const & filename)
104 {
105         string fname = buffer->fileName();
106         string const oldname = fname;
107
108         if (filename.empty()) {
109
110                 FileDialog fileDlg(bv->owner(),
111                                    _("Choose a filename to save document as"),
112                         LFUN_WRITEAS,
113                         make_pair(string(_("Documents|#o#O")),
114                                   string(lyxrc.document_path)),
115                         make_pair(string(_("Templates|#T#t")),
116                                   string(lyxrc.template_path)));
117
118                 if (!IsLyXFilename(fname))
119                         fname += ".lyx";
120
121                 FileDialog::Result result =
122                         fileDlg.Select(OnlyPath(fname),
123                                        _("*.lyx|LyX Documents (*.lyx)"),
124                                        OnlyFilename(fname));
125
126                 if (result.first == FileDialog::Later)
127                         return false;
128
129                 fname = result.second;
130
131                 if (fname.empty())
132                         return false;
133
134                 // Make sure the absolute filename ends with appropriate suffix
135                 fname = MakeAbsPath(fname);
136                 if (!IsLyXFilename(fname))
137                         fname += ".lyx";
138         } else
139                 fname = filename;
140
141         // Same name as we have already?
142         if (!buffer->isUnnamed() && fname == oldname) {
143                 if (!Alert::askQuestion(_("Same name as document already has:"),
144                                  MakeDisplayPath(fname, 50),
145                                  _("Save anyway?")))
146                         return false;
147                 // Falls through to name change and save
148         } 
149         // No, but do we have another file with this name open?
150         else if (!buffer->isUnnamed() && bufferlist.exists(fname)) {
151                 if (Alert::askQuestion(_("Another document with same name open!"),
152                                 MakeDisplayPath(fname, 50),
153                                 _("Replace with current document?")))
154                         {
155                                 bufferlist.close(bufferlist.getBuffer(fname));
156
157                                 // Ok, change the name of the buffer, but don't save!
158                                 buffer->setFileName(fname);
159                                 buffer->markDirty();
160
161                                 ShowMessage(buffer, _("Document renamed to '"),
162                                                 MakeDisplayPath(fname), _("', but not saved..."));
163                 }
164                 return false;
165         } // Check whether the file exists
166         else {
167                 FileInfo const myfile(fname);
168                 if (myfile.isOK() && !Alert::askQuestion(_("Document already exists:"), 
169                                                   MakeDisplayPath(fname, 50),
170                                                   _("Replace file?")))
171                         return false;
172         }
173
174         // Ok, change the name of the buffer
175         buffer->setFileName(fname);
176         buffer->markDirty();
177         bool unnamed = buffer->isUnnamed();
178         buffer->setUnnamed(false);
179
180         if (!MenuWrite(bv, buffer)) {
181             buffer->setFileName(oldname);
182             buffer->setUnnamed(unnamed);
183             ShowMessage(buffer, _("Document could not be saved!"),
184                         _("Holding the old name."), MakeDisplayPath(oldname));
185             return false;
186         }
187         // now remove the oldname autosave file if existant!
188         removeAutosaveFile(oldname);
189         return true;
190 }
191
192
193 int MenuRunChktex(Buffer * buffer)
194 {
195         int ret;
196
197         if (buffer->isSGML()) {
198                 Alert::alert(_("Chktex does not work with SGML derived documents."));
199                 return 0;
200         } else 
201                 ret = buffer->runChktex();
202    
203         if (ret >= 0) {
204                 string s;
205                 string t;
206                 if (ret == 0) {
207                         s = _("No warnings found.");
208                 } else if (ret == 1) {
209                         s = _("One warning found.");
210                         t = _("Use `Navigate->Error' to find it.");
211                 } else {
212                         s += tostr(ret);
213                         s += _(" warnings found.");
214                         t = _("Use `Navigate->Error' to find them.");
215                 }
216                 Alert::alert(_("Chktex run successfully"), s, t);
217         } else {
218                 Alert::alert(_("Error!"), _("It seems chktex does not work."));
219         }
220         return ret;
221 }
222
223
224 void QuitLyX()
225 {
226         lyxerr[Debug::INFO] << "Running QuitLyX." << endl;
227
228         if (lyxrc.use_gui) {
229                 if (!bufferlist.qwriteAll())
230                         return;
231
232                 lastfiles->writeFile(lyxrc.lastfiles);
233         }
234
235         // Set a flag that we do quitting from the program,
236         // so no refreshes are necessary.
237         quitting = true;
238
239         // close buffers first
240         bufferlist.closeAll();
241
242         // do any other cleanup procedures now
243         lyxerr[Debug::INFO] << "Deleting tmp dir " << system_tempdir << endl;
244
245         DestroyLyXTmpDir(system_tempdir);
246
247         finished = true;
248 }
249
250
251
252 void AutoSave(BufferView * bv)
253         // should probably be moved into BufferList (Lgb)
254         // Perfect target for a thread...
255 {
256         if (!bv->available())
257                 return;
258
259         if (bv->buffer()->isBakClean() || bv->buffer()->isReadonly()) {
260                 // We don't save now, but we'll try again later
261                 bv->owner()->resetAutosaveTimer();
262                 return;
263         }
264
265         bv->owner()->message(_("Autosaving current document..."));
266         
267         // create autosave filename
268         string fname =  bv->buffer()->filePath();
269         fname += "#";
270         fname += OnlyFilename(bv->buffer()->fileName());
271         fname += "#";
272         
273         // tmp_ret will be located (usually) in /tmp
274         // will that be a problem?
275         pid_t const pid = fork(); // If you want to debug the autosave
276         // you should set pid to -1, and comment out the
277         // fork.
278         if (pid == 0 || pid == -1) {
279                 // pid = -1 signifies that lyx was unable
280                 // to fork. But we will do the save
281                 // anyway.
282                 bool failed = false;
283                 
284                 string const tmp_ret = lyx::tempName(string(), "lyxauto");
285                 if (!tmp_ret.empty()) {
286                         bv->buffer()->writeFile(tmp_ret, 1);
287                         // assume successful write of tmp_ret
288                         if (!lyx::rename(tmp_ret, fname)) {
289                                 failed = true;
290                                 // most likely couldn't move between filesystems
291                                 // unless write of tmp_ret failed
292                                 // so remove tmp file (if it exists)
293                                 lyx::unlink(tmp_ret);
294                         }
295                 } else {
296                         failed = true;
297                 }
298                 
299                 if (failed) {
300                         // failed to write/rename tmp_ret so try writing direct
301                         if (!bv->buffer()->writeFile(fname, 1)) {
302                                 // It is dangerous to do this in the child,
303                                 // but safe in the parent, so...
304                                 if (pid == -1)
305                                         bv->owner()->message(_("Autosave failed!"));
306                         }
307                 }
308                 if (pid == 0) { // we are the child so...
309                         _exit(0);
310                 }
311         }
312         
313         bv->buffer()->markBakClean();
314         bv->owner()->resetAutosaveTimer();
315 }
316
317
318 //
319 // Copyright CHT Software Service GmbH
320 // Uwe C. Schroeder
321 //
322 // create new file with template
323 // SERVERCMD !
324 //
325 Buffer * NewLyxFile(string const & filename)
326 {
327         // Split argument by :
328         string name;
329         string tmpname = split(filename, name, ':');
330 #ifdef __EMX__ // Fix me! lyx_cb.C may not be low level enough to allow this.
331         if (name.length() == 1
332             && isalpha(static_cast<unsigned char>(name[0]))
333             && (prefixIs(tmpname, "/") || prefixIs(tmpname, "\\"))) {
334                 name += ':';
335                 name += token(tmpname, ':', 0);
336                 tmpname = split(tmpname, ':');
337         }
338 #endif
339         lyxerr[Debug::INFO] << "Arg is " << filename
340                             << "\nName is " << name
341                             << "\nTemplate is " << tmpname << endl;
342
343         // find a free buffer 
344         Buffer * tmpbuf = bufferlist.newFile(name, tmpname);
345         if (tmpbuf)
346                 lastfiles->newFile(tmpbuf->fileName());
347         return tmpbuf;
348 }
349
350
351 // Insert ascii file (if filename is empty, prompt for one)
352 void InsertAsciiFile(BufferView * bv, string const & f, bool asParagraph)
353 {
354         string fname = f;
355
356         if (!bv->available()) 
357                 return;
358      
359         if (fname.empty()) {
360                 FileDialog fileDlg(bv->owner(), _("Select file to insert"),
361                         (asParagraph) ? LFUN_FILE_INSERT_ASCII_PARA : LFUN_FILE_INSERT_ASCII);
362  
363                 FileDialog::Result result = fileDlg.Select(bv->owner()->buffer()->filePath());
364
365                 if (result.first == FileDialog::Later)
366                         return;
367
368                 fname = result.second;
369
370                 if (fname.empty()) 
371                         return;
372         }
373
374         FileInfo fi(fname);
375
376         if (!fi.readable()) {
377                 Alert::err_alert(_("Error! Specified file is unreadable: "),
378                              MakeDisplayPath(fname, 50));
379                 return;
380         }
381
382         ifstream ifs(fname.c_str());
383         if (!ifs) {
384                 Alert::err_alert(_("Error! Cannot open specified file: "),
385                              MakeDisplayPath(fname, 50));
386                 return;
387         }
388
389         ifs.unsetf(ios::skipws);
390         istream_iterator<char> ii(ifs);
391         istream_iterator<char> end;
392 #if !defined(USE_INCLUDED_STRING) && !defined(STD_STRING_IS_GOOD)
393         // We use this until the compilers get better...
394         vector<char> tmp;
395         copy(ii, end, back_inserter(tmp));
396         string const tmpstr(tmp.begin(), tmp.end());
397 #else
398         // This is what we want to use and what we will use once the
399         // compilers get good enough. 
400         //string tmpstr(ii, end); // yet a reason for using std::string
401         // alternate approach to get the file into a string:
402         string tmpstr;
403         copy(ii, end, back_inserter(tmpstr));
404 #endif
405         // insert the string
406         bv->hideCursor();
407         
408         // clear the selection
409         bv->beforeChange(bv->text);
410         if (!asParagraph)
411                 bv->text->insertStringAsLines(bv, tmpstr);
412         else
413                 bv->text->insertStringAsParagraphs(bv, tmpstr);
414         bv->update(bv->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
415 }
416
417
418 void MenuInsertLabel(BufferView * bv, string const & arg)
419 {
420         string label(arg);
421         bv->owner()->prohibitInput();
422         if (label.empty()) {
423                 Paragraph * par = bv->getLyXText()->cursor.par();
424                 LyXTextClass const & tclass =
425                         textclasslist[bv->buffer()->params.textclass];
426                 LyXLayout const * layout = &tclass[par->layout()];
427
428                 if (layout->latextype == LATEX_PARAGRAPH && par->previous()) {
429                         Paragraph * par2 = par->previous();
430                         LyXLayout const * layout2 = &tclass[par2->layout()];
431                         if (layout2->latextype != LATEX_PARAGRAPH) {
432                                 par = par2;
433                                 layout = layout2;
434                         }
435                 }
436                 string text = layout->latexname().substr(0, 3);
437                 if (layout->latexname() == "theorem")
438                         text = "thm"; // Create a correct prefix for prettyref
439
440                 text += ":";
441                 if (layout->latextype == LATEX_PARAGRAPH ||
442                     lyxrc.label_init_length < 0)
443                         text.erase();
444                 string par_text = par->asString(bv->buffer(), false);
445                 for (int i = 0; i < lyxrc.label_init_length; ++i) {
446                         if (par_text.empty())
447                                 break;
448                         string head;
449                         par_text = split(par_text, head, ' ');
450                         if (i > 0)
451                                 text += '-'; // Is it legal to use spaces in
452                                              // labels ?
453                         text += head;
454                 }
455
456                 pair<bool, string> result =
457                         Alert::askForText(_("Enter new label to insert:"), text);
458                 if (result.first) {
459                         label = frontStrip(strip(result.second));
460                 }
461         }
462         if (!label.empty()) {
463                 InsetCommandParams p("label", label);
464                 InsetLabel * inset = new InsetLabel(p);
465                 bv->insertInset(inset);
466         }
467         bv->owner()->allowInput();
468 }
469
470
471 // This function runs "configure" and then rereads lyx.defaults to
472 // reconfigure the automatic settings.
473 void Reconfigure(BufferView * bv)
474 {
475         bv->owner()->message(_("Running configure..."));
476
477         // Run configure in user lyx directory
478         Path p(user_lyxdir);
479         Systemcall one;
480         one.startscript(Systemcall::Wait, 
481                         AddName(system_lyxdir, "configure"));
482         p.pop();
483         bv->owner()->message(_("Reloading configuration..."));
484         lyxrc.read(LibFileSearch(string(), "lyxrc.defaults"));
485         Alert::alert(_("The system has been reconfigured."), 
486                    _("You need to restart LyX to make use of any"),
487                    _("updated document class specifications."));
488 }