]> git.lyx.org Git - lyx.git/blob - src/Session.cpp
Avoid full metrics computation with Update:FitCursor
[lyx.git] / src / Session.cpp
1 /**
2  * \file Session.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  * \author Bo Peng
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "Session.h"
15
16 #include "support/debug.h"
17 #include "support/filetools.h"
18 #include "support/Package.h"
19
20 #include <fstream>
21 #include <sstream>
22 #include <algorithm>
23 #include <iterator>
24
25 using namespace std;
26 using namespace lyx::support;
27
28 namespace {
29
30 string const sec_lastfiles = "[recent files]";
31 string const sec_lastfilepos = "[cursor positions]";
32 string const sec_lastopened = "[last opened files]";
33 string const sec_bookmarks = "[bookmarks]";
34 string const sec_lastcommands = "[last commands]";
35 string const sec_authfiles = "[auth files]";
36 string const sec_shellescape = "[shell escape files]";
37 // currently unused:
38 //string const sec_session = "[session info]";
39 //string const sec_toolbars = "[toolbars]";
40
41 } // namespace
42
43
44 namespace lyx {
45
46 LastFilesSection::LastFilesSection(unsigned int num) :
47         default_num_last_files(4),
48         absolute_max_last_files(100)
49 {
50         setNumberOfLastFiles(num);
51 }
52
53
54 void LastFilesSection::read(istream & is)
55 {
56         string tmp;
57         do {
58                 char c = is.peek();
59                 if (c == '[')
60                         break;
61                 getline(is, tmp);
62                 if (tmp.empty() || tmp[0] == '#' || tmp[0] == ' ' || !FileName::isAbsolute(tmp))
63                         continue;
64
65                 // read lastfiles
66                 FileName const file(tmp);
67                 if (file.exists() && !file.isDirectory()
68                     && lastfiles.size() < num_lastfiles)
69                         lastfiles.push_back(file);
70                 else
71                         LYXERR(Debug::INIT, "LyX: Warning: Ignore last file: " << tmp);
72         } while (is.good());
73 }
74
75
76 void LastFilesSection::write(ostream & os) const
77 {
78         os << '\n' << sec_lastfiles << '\n';
79         copy(lastfiles.begin(), lastfiles.end(),
80              ostream_iterator<FileName>(os, "\n"));
81 }
82
83
84 void LastFilesSection::add(FileName const & file)
85 {
86         // If file already exist, delete it and reinsert at front.
87         LastFiles::iterator it = find(lastfiles.begin(), lastfiles.end(), file);
88         if (it != lastfiles.end())
89                 lastfiles.erase(it);
90         lastfiles.insert(lastfiles.begin(), file);
91         if (lastfiles.size() > num_lastfiles)
92                 lastfiles.pop_back();
93 }
94
95
96 void LastFilesSection::setNumberOfLastFiles(unsigned int no)
97 {
98         if (0 < no && no <= absolute_max_last_files)
99                 num_lastfiles = no;
100         else {
101                 LYXERR(Debug::INIT, "LyX: session: too many last files\n"
102                         << "\tdefault (=" << default_num_last_files << ") used.");
103                 num_lastfiles = default_num_last_files;
104         }
105 }
106
107
108 void LastOpenedSection::read(istream & is)
109 {
110         string tmp;
111         do {
112                 char c = is.peek();
113                 if (c == '[')
114                         break;
115                 getline(is, tmp);
116                 if (tmp.empty() || tmp[0] == '#' || tmp[0] == ' ')
117                         continue;
118
119                 try {
120                         LastOpenedFile lof;
121                         istringstream itmp(tmp);
122                         itmp >> lof.active;
123                         itmp.ignore(2);  // ignore ", "
124                         string fname;
125                         getline(itmp, fname);
126                         if (!FileName::isAbsolute(fname))
127                                 continue;
128
129                         FileName const file(fname);
130                         if (file.exists() && !file.isDirectory()) {
131                                 lof.file_name = file;
132                                 lastopened.push_back(lof);
133                         } else {
134                                 LYXERR(Debug::INIT,
135                                         "LyX: Warning: Ignore last opened file: " << tmp);
136                         }
137                 } catch (...) {
138                         LYXERR(Debug::INIT,
139                                 "LyX: Warning: unknown state of last opened file: " << tmp);
140                 }
141         } while (is.good());
142 }
143
144
145 void LastOpenedSection::write(ostream & os) const
146 {
147         os << '\n' << sec_lastopened << '\n';
148         for (auto const & last : lastopened)
149                 os << last.active << ", " << last.file_name << '\n';
150 }
151
152
153 void LastOpenedSection::add(FileName const & file, bool active)
154 {
155         LastOpenedFile lof(file, active);
156         // check if file is already recorded (this can happen
157         // with multiple buffer views). We do only record each
158         // file once, since we cannot restore multiple views
159         // currently, we even crash in some cases (see #9483).
160         // FIXME: Add session support for multiple views of
161         //        the same buffer (split-view etc.).
162         for (auto const & last : lastopened) {
163                 if (last.file_name == file)
164                         return;
165         }
166         lastopened.push_back(lof);
167 }
168
169
170 void LastOpenedSection::clear()
171 {
172         lastopened.clear();
173 }
174
175
176 void LastFilePosSection::read(istream & is)
177 {
178         string tmp;
179         do {
180                 char c = is.peek();
181                 if (c == '[')
182                         break;
183                 getline(is, tmp);
184                 if (tmp == "" || tmp[0] == '#' || tmp[0] == ' ')
185                         continue;
186
187                 try {
188                         // read lastfilepos
189                         // pos, file\n
190                         FilePos filepos;
191                         string fname;
192                         istringstream itmp(tmp);
193                         itmp >> filepos.pit;
194                         itmp.ignore(2);  // ignore ", "
195                         itmp >> filepos.pos;
196                         itmp.ignore(2);  // ignore ", "
197                         getline(itmp, fname);
198                         if (!FileName::isAbsolute(fname))
199                                 continue;
200                         filepos.file = FileName(fname);
201                         if (filepos.file.exists() && !filepos.file.isDirectory()
202                             && lastfilepos.size() < num_lastfilepos)
203                                 lastfilepos.push_back(filepos);
204                         else
205                                 LYXERR(Debug::INIT, "LyX: Warning: Ignore pos of last file: " << fname);
206                 } catch (...) {
207                         LYXERR(Debug::INIT, "LyX: Warning: unknown pos of last file: " << tmp);
208                 }
209         } while (is.good());
210 }
211
212
213 void LastFilePosSection::write(ostream & os) const
214 {
215         os << '\n' << sec_lastfilepos << '\n';
216         for (auto const & file_p : lastfilepos)
217                 os << file_p.pit << ", " << file_p.pos << ", " << file_p.file << '\n';
218 }
219
220
221 void LastFilePosSection::save(FilePos const & pos)
222 {
223         // Remove element if it was already present. Iterating should
224         // not be a problem since the list is small (<100 elements).
225         for (FilePosList::iterator it = lastfilepos.begin();
226              it != lastfilepos.end(); ++it)
227                 if (it->file == pos.file) {
228                         lastfilepos.erase(it);
229                         break;
230                 }
231
232         // insert new element at front.
233         lastfilepos.push_front(pos);
234 }
235
236
237 LastFilePosSection::FilePos LastFilePosSection::load(FileName const & fname) const
238 {
239         for (auto const & fp : lastfilepos)
240                 if (fp.file == fname)
241                         // Has position information, return it.
242                         return fp;
243
244         // Not found, return the first paragraph
245         return FilePos();
246 }
247
248
249 void BookmarksSection::clear()
250 {
251         // keep bookmark[0], the temporary one
252         bookmarks.resize(1);
253         bookmarks.resize(max_bookmarks + 1);
254 }
255
256
257 void BookmarksSection::read(istream & is)
258 {
259         string tmp;
260         do {
261                 char c = is.peek();
262                 if (c == '[')
263                         break;
264                 getline(is, tmp);
265                 if (tmp == "" || tmp[0] == '#' || tmp[0] == ' ')
266                         continue;
267
268                 try {
269                         // read bookmarks
270                         // idx, pit, pos, file\n
271                         unsigned int idx;
272                         pit_type pit;
273                         pos_type pos;
274                         string fname;
275                         istringstream itmp(tmp);
276                         itmp >> idx;
277                         itmp.ignore(2);  // ignore ", "
278                         itmp >> pit;
279                         itmp.ignore(2);  // ignore ", "
280                         itmp >> pos;
281                         itmp.ignore(2);  // ignore ", "
282                         getline(itmp, fname);
283                         if (!FileName::isAbsolute(fname))
284                                 continue;
285                         FileName const file(fname);
286                         // only load valid bookmarks
287                         if (file.exists() && !file.isDirectory() && idx < bookmarks.size())
288                                 bookmarks[idx] = Bookmark(file, pit, pos, 0, 0);
289                         else
290                                 LYXERR(Debug::INIT, "LyX: Warning: Ignore bookmark of file: " << fname);
291                 } catch (...) {
292                         LYXERR(Debug::INIT, "LyX: Warning: unknown Bookmark info: " << tmp);
293                 }
294         } while (is.good());
295 }
296
297
298 void BookmarksSection::write(ostream & os) const
299 {
300         os << '\n' << sec_bookmarks << '\n';
301         for (size_t i = 0; i < bookmarks.size(); ++i) {
302                 if (isValid(i))
303                         os << i << ", "
304                            << bookmarks[i].bottom_pit << ", "
305                            << bookmarks[i].bottom_pos << ", "
306                            << bookmarks[i].filename << '\n';
307         }
308 }
309
310
311 void BookmarksSection::save(FileName const & fname,
312         pit_type bottom_pit, pos_type bottom_pos,
313         int top_id, pos_type top_pos, unsigned int idx)
314 {
315         // silently ignore bookmarks when idx is out of range
316         if (idx < bookmarks.size())
317                 bookmarks[idx] = Bookmark(fname, bottom_pit, bottom_pos, top_id, top_pos);
318 }
319
320
321 bool BookmarksSection::isValid(unsigned int i) const
322 {
323         return i < bookmarks.size() && !bookmarks[i].filename.empty();
324 }
325
326
327 bool BookmarksSection::hasValid() const
328 {
329         for (size_t i = 1; i < bookmarks.size(); ++i) {
330                 if (isValid(i))
331                         return true;
332         }
333         return false;
334 }
335
336
337 BookmarksSection::Bookmark const & BookmarksSection::bookmark(unsigned int i) const
338 {
339         return bookmarks[i];
340 }
341
342
343 BookmarksSection::BookmarkPosList
344 BookmarksSection::bookmarksInPar(FileName const & fn, int const par_id) const
345 {
346         // FIXME: we do not consider the case of bottom_pit.
347         // This is probably not a problem.
348         BookmarksSection::BookmarkPosList bip;
349         for (size_t i = 1; i < bookmarks.size(); ++i)
350                 if (bookmarks[i].filename == fn && bookmarks[i].top_id == par_id)
351                         bip.push_back({i, bookmarks[i].top_pos});
352
353         return bip;
354 }
355
356
357 void BookmarksSection::adjustPosAfterPos(FileName const & fn,
358         int const par_id, pos_type pos, int offset)
359 {
360         for (Bookmark & bkm : bookmarks)
361                 if (bkm.filename == fn && bkm.top_id == par_id && bkm.top_pos > pos)
362                         bkm.top_pos += offset;
363 }
364
365
366 LastCommandsSection::LastCommandsSection(unsigned int num) :
367         default_num_last_commands(30),
368         absolute_max_last_commands(100)
369 {
370         setNumberOfLastCommands(num);
371 }
372
373
374 void LastCommandsSection::read(istream & is)
375 {
376         string tmp;
377         do {
378                 char c = is.peek();
379                 if (c == '[')
380                         break;
381                 getline(is, tmp);
382                 if (tmp == "" || tmp[0] == '#' || tmp[0] == ' ')
383                         continue;
384
385                 lastcommands.push_back(tmp);
386         } while (is.good());
387 }
388
389
390 void LastCommandsSection::write(ostream & os) const
391 {
392         os << '\n' << sec_lastcommands << '\n';
393         copy(lastcommands.begin(), lastcommands.end(),
394                 ostream_iterator<std::string>(os, "\n"));
395 }
396
397
398 void LastCommandsSection::setNumberOfLastCommands(unsigned int no)
399 {
400         if (0 < no && no <= absolute_max_last_commands)
401                 num_lastcommands = no;
402         else {
403                 LYXERR(Debug::INIT, "LyX: session: too many last commands\n"
404                         << "\tdefault (=" << default_num_last_commands << ") used.");
405                 num_lastcommands = default_num_last_commands;
406         }
407 }
408
409
410 void LastCommandsSection::add(std::string const & command)
411 {
412         // remove traces of 'command' in history using the erase-remove idiom
413         //   https://en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom
414         lastcommands.erase(remove(lastcommands.begin(), lastcommands.end(), command),
415                            lastcommands.end());
416         // add it at the end of the list.
417         lastcommands.push_back(command);
418 }
419
420
421 void LastCommandsSection::clear()
422 {
423         lastcommands.clear();
424 }
425
426
427 Session::Session(unsigned int num_last_files, unsigned int num_last_commands) :
428         last_files(num_last_files), last_commands(num_last_commands)
429 {
430         // locate the session file
431         // note that the session file name 'session' is hard-coded
432         session_file = FileName(addName(package().user_support().absFileName(), "session"));
433         //
434         readFile();
435 }
436
437
438 void Session::readFile()
439 {
440         // we will not complain if we can't find session_file nor will
441         // we issue a warning. (Lgb)
442         ifstream is(session_file.toFilesystemEncoding().c_str());
443         string tmp;
444
445         while (getline(is, tmp)) {
446                 // Ignore comments, empty line or line stats with ' '
447                 if (tmp == "" || tmp[0] == '#' || tmp[0] == ' ')
448                         continue;
449
450                 // Determine section id
451                 if (tmp == sec_lastfiles)
452                         lastFiles().read(is);
453                 else if (tmp == sec_lastopened)
454                         lastOpened().read(is);
455                 else if (tmp == sec_lastfilepos)
456                         lastFilePos().read(is);
457                 else if (tmp == sec_bookmarks)
458                         bookmarks().read(is);
459                 else if (tmp == sec_lastcommands)
460                         lastCommands().read(is);
461                 else if (tmp == sec_authfiles)
462                         authFiles().read(is);
463                 else if (tmp == sec_shellescape)
464                         shellescapeFiles().read(is);
465
466                 else
467                         LYXERR(Debug::INIT, "LyX: Warning: unknown Session section: " << tmp);
468         }
469 }
470
471
472 void Session::writeFile() const
473 {
474         ofstream os(session_file.toFilesystemEncoding().c_str());
475         if (os) {
476                 os << "## Automatically generated lyx session file \n"
477                     << "## Editing this file manually may cause lyx to crash.\n";
478
479                 lastFiles().write(os);
480                 lastOpened().write(os);
481                 lastFilePos().write(os);
482                 lastCommands().write(os);
483                 bookmarks().write(os);
484                 authFiles().write(os);
485                 shellescapeFiles().write(os);
486         } else
487                 LYXERR(Debug::INIT, "LyX: Warning: unable to save Session: "
488                        << session_file);
489 }
490
491
492 AuthFilesSection::AuthFilesSection() {  }
493
494
495 void AuthFilesSection::read(istream & is)
496 {
497         string tmp;
498         do {
499                 char c = is.peek();
500                 if (c == '[')
501                         break;
502                 getline(is, tmp);
503                 if (tmp.empty() || tmp[0] == '#' || tmp[0] == ' ' || !FileName::isAbsolute(tmp))
504                         continue;
505
506                 // read lastfiles
507                 FileName const file(tmp);
508                 if (file.exists() && !file.isDirectory())
509                         auth_files_.insert(tmp);
510                 else
511                         LYXERR(Debug::INIT, "LyX: Warning: Ignore auth file: " << tmp);
512         } while (is.good());
513 }
514
515
516 void AuthFilesSection::write(ostream & os) const
517 {
518         os << '\n' << sec_authfiles << '\n';
519         copy(auth_files_.begin(), auth_files_.end(),
520              ostream_iterator<std::string>(os, "\n"));
521 }
522
523
524 bool AuthFilesSection::find(string const & name) const
525 {
526         return auth_files_.find(name) != auth_files_.end();
527 }
528
529
530 void AuthFilesSection::insert(string const & name)
531 {
532         auth_files_.insert(name);
533 }
534
535
536 void ShellEscapeSection::read(istream & is)
537 {
538         string s;
539         do {
540                 char c = is.peek();
541                 if (c == '[')
542                         break;
543                 getline(is, s);
544                 if (s.empty() || s[0] == '#' || s[0] == ' ' || !FileName::isAbsolute(s))
545                         continue;
546
547                 // read shellescape files
548                 FileName const file(s.substr(0, s.length() - 2));
549                 if (file.exists() && !file.isDirectory())
550                         shellescape_files_.insert(s);
551                 else
552                         LYXERR(Debug::INIT, "LyX: Warning: Ignore shellescape file: " << file);
553         } while (is.good());
554 }
555
556
557 void ShellEscapeSection::write(ostream & os) const
558 {
559         os << '\n' << sec_shellescape << '\n';
560         copy(shellescape_files_.begin(), shellescape_files_.end(),
561              ostream_iterator<std::string>(os, "\n"));
562 }
563
564
565 bool ShellEscapeSection::find(string const & name) const
566 {
567         if (shellescape_files_.find(name + ",0") != shellescape_files_.end())
568                 return true;
569
570         return findAuth(name);
571 }
572
573
574 bool ShellEscapeSection::findAuth(string const & name) const
575 {
576         return shellescape_files_.find(name + ",1") != shellescape_files_.end();
577 }
578
579
580 void ShellEscapeSection::insert(string const & name, bool auth)
581 {
582         set<string>::iterator it;
583         string const name0 = name + ",0";
584         string const name1 = name + ",1";
585
586         if (auth) {
587                 it = shellescape_files_.find(name0);
588                 if (it != shellescape_files_.end())
589                         shellescape_files_.erase(it);
590                 shellescape_files_.insert(name1);
591         } else {
592                 it = shellescape_files_.find(name1);
593                 if (it != shellescape_files_.end())
594                         shellescape_files_.erase(it);
595                 shellescape_files_.insert(name0);
596         }
597 }
598
599
600 void ShellEscapeSection::remove(string const & name)
601 {
602         set<string>::iterator it = shellescape_files_.find(name + ",0");
603         if (it == shellescape_files_.end())
604                 it = shellescape_files_.find(name + ",1");
605         if (it != shellescape_files_.end())
606                 shellescape_files_.erase(it);
607 }
608
609
610 } // namespace lyx