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