]> git.lyx.org Git - lyx.git/blob - src/Session.cpp
Adjust bookmark position when inserting/deleting from paragraph
[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 void BookmarksSection::adjustPosAfterPos(FileName const & fn,
357         int const par_id, pos_type pos, int offset)
358 {
359         for (Bookmark & bkm : bookmarks)
360                 if (bkm.filename == fn && bkm.top_id == par_id && bkm.top_pos > pos)
361                         bkm.top_pos += offset;
362 }
363
364
365 LastCommandsSection::LastCommandsSection(unsigned int num) :
366         default_num_last_commands(30),
367         absolute_max_last_commands(100)
368 {
369         setNumberOfLastCommands(num);
370 }
371
372
373 void LastCommandsSection::read(istream & is)
374 {
375         string tmp;
376         do {
377                 char c = is.peek();
378                 if (c == '[')
379                         break;
380                 getline(is, tmp);
381                 if (tmp == "" || tmp[0] == '#' || tmp[0] == ' ')
382                         continue;
383
384                 lastcommands.push_back(tmp);
385         } while (is.good());
386 }
387
388
389 void LastCommandsSection::write(ostream & os) const
390 {
391         os << '\n' << sec_lastcommands << '\n';
392         copy(lastcommands.begin(), lastcommands.end(),
393                 ostream_iterator<std::string>(os, "\n"));
394 }
395
396
397 void LastCommandsSection::setNumberOfLastCommands(unsigned int no)
398 {
399         if (0 < no && no <= absolute_max_last_commands)
400                 num_lastcommands = no;
401         else {
402                 LYXERR(Debug::INIT, "LyX: session: too many last commands\n"
403                         << "\tdefault (=" << default_num_last_commands << ") used.");
404                 num_lastcommands = default_num_last_commands;
405         }
406 }
407
408
409 void LastCommandsSection::add(std::string const & command)
410 {
411         lastcommands.push_back(command);
412 }
413
414
415 void LastCommandsSection::clear()
416 {
417         lastcommands.clear();
418 }
419
420
421 Session::Session(unsigned int num_last_files, unsigned int num_last_commands) :
422         last_files(num_last_files), last_commands(num_last_commands)
423 {
424         // locate the session file
425         // note that the session file name 'session' is hard-coded
426         session_file = FileName(addName(package().user_support().absFileName(), "session"));
427         //
428         readFile();
429 }
430
431
432 void Session::readFile()
433 {
434         // we will not complain if we can't find session_file nor will
435         // we issue a warning. (Lgb)
436         ifstream is(session_file.toFilesystemEncoding().c_str());
437         string tmp;
438
439         while (getline(is, tmp)) {
440                 // Ignore comments, empty line or line stats with ' '
441                 if (tmp == "" || tmp[0] == '#' || tmp[0] == ' ')
442                         continue;
443
444                 // Determine section id
445                 if (tmp == sec_lastfiles)
446                         lastFiles().read(is);
447                 else if (tmp == sec_lastopened)
448                         lastOpened().read(is);
449                 else if (tmp == sec_lastfilepos)
450                         lastFilePos().read(is);
451                 else if (tmp == sec_bookmarks)
452                         bookmarks().read(is);
453                 else if (tmp == sec_lastcommands)
454                         lastCommands().read(is);
455                 else if (tmp == sec_authfiles)
456                         authFiles().read(is);
457                 else if (tmp == sec_shellescape)
458                         shellescapeFiles().read(is);
459
460                 else
461                         LYXERR(Debug::INIT, "LyX: Warning: unknown Session section: " << tmp);
462         }
463 }
464
465
466 void Session::writeFile() const
467 {
468         ofstream os(session_file.toFilesystemEncoding().c_str());
469         if (os) {
470                 os << "## Automatically generated lyx session file \n"
471                     << "## Editing this file manually may cause lyx to crash.\n";
472
473                 lastFiles().write(os);
474                 lastOpened().write(os);
475                 lastFilePos().write(os);
476                 lastCommands().write(os);
477                 bookmarks().write(os);
478                 authFiles().write(os);
479                 shellescapeFiles().write(os);
480         } else
481                 LYXERR(Debug::INIT, "LyX: Warning: unable to save Session: "
482                        << session_file);
483 }
484
485
486 AuthFilesSection::AuthFilesSection() {  }
487
488
489 void AuthFilesSection::read(istream & is)
490 {
491         string tmp;
492         do {
493                 char c = is.peek();
494                 if (c == '[')
495                         break;
496                 getline(is, tmp);
497                 if (tmp.empty() || tmp[0] == '#' || tmp[0] == ' ' || !FileName::isAbsolute(tmp))
498                         continue;
499
500                 // read lastfiles
501                 FileName const file(tmp);
502                 if (file.exists() && !file.isDirectory())
503                         auth_files_.insert(tmp);
504                 else
505                         LYXERR(Debug::INIT, "LyX: Warning: Ignore auth file: " << tmp);
506         } while (is.good());
507 }
508
509
510 void AuthFilesSection::write(ostream & os) const
511 {
512         os << '\n' << sec_authfiles << '\n';
513         copy(auth_files_.begin(), auth_files_.end(),
514              ostream_iterator<std::string>(os, "\n"));
515 }
516
517
518 bool AuthFilesSection::find(string const & name) const
519 {
520         return auth_files_.find(name) != auth_files_.end();
521 }
522
523
524 void AuthFilesSection::insert(string const & name)
525 {
526         auth_files_.insert(name);
527 }
528
529
530 void ShellEscapeSection::read(istream & is)
531 {
532         string s;
533         do {
534                 char c = is.peek();
535                 if (c == '[')
536                         break;
537                 getline(is, s);
538                 if (s.empty() || s[0] == '#' || s[0] == ' ' || !FileName::isAbsolute(s))
539                         continue;
540
541                 // read shellescape files
542                 FileName const file(s.substr(0, s.length() - 2));
543                 if (file.exists() && !file.isDirectory())
544                         shellescape_files_.insert(s);
545                 else
546                         LYXERR(Debug::INIT, "LyX: Warning: Ignore shellescape file: " << file);
547         } while (is.good());
548 }
549
550
551 void ShellEscapeSection::write(ostream & os) const
552 {
553         os << '\n' << sec_shellescape << '\n';
554         copy(shellescape_files_.begin(), shellescape_files_.end(),
555              ostream_iterator<std::string>(os, "\n"));
556 }
557
558
559 bool ShellEscapeSection::find(string const & name) const
560 {
561         if (shellescape_files_.find(name + ",0") != shellescape_files_.end())
562                 return true;
563
564         return findAuth(name);
565 }
566
567
568 bool ShellEscapeSection::findAuth(string const & name) const
569 {
570         return shellescape_files_.find(name + ",1") != shellescape_files_.end();
571 }
572
573
574 void ShellEscapeSection::insert(string const & name, bool auth)
575 {
576         set<string>::iterator it;
577         string const name0 = name + ",0";
578         string const name1 = name + ",1";
579
580         if (auth) {
581                 it = shellescape_files_.find(name0);
582                 if (it != shellescape_files_.end())
583                         shellescape_files_.erase(it);
584                 shellescape_files_.insert(name1);
585         } else {
586                 it = shellescape_files_.find(name1);
587                 if (it != shellescape_files_.end())
588                         shellescape_files_.erase(it);
589                 shellescape_files_.insert(name0);
590         }
591 }
592
593
594 void ShellEscapeSection::remove(string const & name)
595 {
596         set<string>::iterator it = shellescape_files_.find(name + ",0");
597         if (it == shellescape_files_.end())
598                 it = shellescape_files_.find(name + ",1");
599         if (it != shellescape_files_.end())
600                 shellescape_files_.erase(it);
601 }
602
603
604 } // namespace lyx