]> git.lyx.org Git - lyx.git/blob - src/session.C
New bookmarks implementation:
[lyx.git] / src / session.C
1 /**
2  * \file session.C
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 #include "debug.h"
16 #include "support/package.h"
17 #include "support/filetools.h"
18
19 #include <boost/filesystem/operations.hpp>
20
21 #include <fstream>
22 #include <sstream>
23 #include <algorithm>
24 #include <iterator>
25
26 using lyx::support::addName;
27 using lyx::support::package;
28
29 namespace fs = boost::filesystem;
30
31 using std::vector;
32 using std::getline;
33 using std::string;
34 using std::ifstream;
35 using std::ofstream;
36 using std::istream;
37 using std::ostream;
38 using std::endl;
39 using std::istringstream;
40 using std::copy;
41 using std::find;
42 using std::ostream_iterator;
43
44 namespace {
45
46 string const sec_lastfiles = "[recent files]";
47 string const sec_lastfilepos = "[cursor positions]";
48 string const sec_lastopened = "[last opened files]";
49 string const sec_bookmarks = "[bookmarks]";
50 string const sec_session = "[session info]";
51
52 } // anon namespace
53
54
55 namespace lyx {
56
57 LastFilesSection::LastFilesSection(unsigned int num) :
58         default_num_last_files(4),
59         absolute_max_last_files(100)
60 {
61         setNumberOfLastFiles(num);
62 }
63
64
65 void LastFilesSection::read(istream & is)
66 {
67         string tmp;
68         do {
69                 char c = is.peek();
70                 if (c == '[')
71                         break;
72                 getline(is, tmp);
73                 // read lastfiles
74                 if (!fs::exists(tmp) || lastfiles.size() >= num_lastfiles)
75                         continue;
76                 lastfiles.push_back(tmp);
77         } while (is.good());
78 }
79
80
81 void LastFilesSection::write(ostream & os) const
82 {
83         os << '\n' << sec_lastfiles << '\n';
84         copy(lastfiles.begin(), lastfiles.end(),
85              ostream_iterator<string>(os, "\n"));
86 }
87
88
89 void LastFilesSection::add(string const & file)
90 {
91         // If file already exist, delete it and reinsert at front.
92         LastFiles::iterator it = find(lastfiles.begin(), lastfiles.end(), file);
93         if (it != lastfiles.end())
94                 lastfiles.erase(it);
95         lastfiles.push_front(file);
96         if (lastfiles.size() > num_lastfiles)
97                 lastfiles.pop_back();
98 }
99
100
101 void LastFilesSection::setNumberOfLastFiles(unsigned int no)
102 {
103         if (0 < no && no <= absolute_max_last_files)
104                 num_lastfiles = no;
105         else {
106                 lyxerr << "LyX: session: too many last files\n"
107                        << "\tdefault (=" << default_num_last_files
108                        << ") used." << endl;
109                 num_lastfiles = default_num_last_files;
110         }
111 }
112
113
114 void LastOpenedSection::read(istream & is)
115 {
116         string tmp;
117         do {
118                 char c = is.peek();
119                 if (c == '[')
120                         break;
121                 getline(is, tmp);
122                 if (!fs::exists(tmp))
123                         continue;
124                 lastopened.push_back(tmp);
125         } while (is.good());
126 }
127
128
129 void LastOpenedSection::write(ostream & os) const
130 {
131         os << '\n' << sec_lastopened << '\n';
132         copy(lastopened.begin(), lastopened.end(),
133              ostream_iterator<string>(os, "\n"));
134 }
135
136
137 void LastOpenedSection::add(string const & file)
138 {
139         lastopened.push_back(file);
140 }
141
142
143 void LastOpenedSection::clear()
144 {
145         lastopened.clear();
146 }
147
148
149 void LastFilePosSection::read(istream & is)
150 {
151         string tmp;
152         do {
153                 char c = is.peek();
154                 if (c == '[')
155                         break;
156                 getline(is, tmp);
157                 // read lastfilepos
158                 // pos, file\n
159                 pit_type pit;
160                 pos_type pos;
161                 string fname;
162                 istringstream itmp(tmp);
163                 itmp >> pit;
164                 itmp.ignore(2);  // ignore ", "
165                 itmp >> pos;
166                 itmp.ignore(2);  // ignore ", "
167                 itmp >> fname;
168                 if (!fs::exists(fname) || lastfilepos.size() >= num_lastfilepos)
169                         continue;
170                 lastfilepos[fname] = boost::tie(pit, pos);
171         } while (is.good());
172 }
173
174
175 void LastFilePosSection::write(ostream & os) const
176 {
177         os << '\n' << sec_lastfilepos << '\n';
178         for (FilePosMap::const_iterator file = lastfilepos.begin();
179                 file != lastfilepos.end(); ++file) {
180                 os << file->second.get<0>() << ", "
181                     << file->second.get<1>() << ", "
182                     << file->first << '\n';
183         }
184 }
185
186
187 void LastFilePosSection::save(string const & fname, FilePos pos)
188 {
189         lastfilepos[fname] = pos;
190 }
191
192
193 LastFilePosSection::FilePos LastFilePosSection::load(string const & fname) const
194 {
195         FilePosMap::const_iterator entry = lastfilepos.find(fname);
196         // Has position information, return it.
197         if (entry != lastfilepos.end())
198                 return entry->second;
199         // Not found, return the first paragraph
200         else
201                 return 0;
202 }
203
204
205 void BookmarksSection::read(istream & is)
206 {
207         string tmp;
208         do {
209                 char c = is.peek();
210                 if (c == '[')
211                         break;
212                 getline(is, tmp);
213                 // read bookmarks
214                 // id, pos, file\n
215                 unsigned int id;
216                 pos_type pos;
217                 string fname;
218                 istringstream itmp(tmp);
219                 itmp >> id;
220                 itmp.ignore(2);  // ignore ", "
221                 itmp >> pos;
222                 itmp.ignore(2);  // ignore ", "
223                 itmp >> fname;
224                 // only load valid bookmarks
225                 if (bookmarks.size() < max_bookmarks && fs::exists(fname))
226                         bookmarks.push_back(Bookmark(fname, id, pos));
227         } while (is.good());
228 }
229
230
231 void BookmarksSection::write(ostream & os) const
232 {
233         os << '\n' << sec_bookmarks << '\n';
234         for (size_t i = 0; i < bookmarks.size(); ++i) {
235                 os << bookmarks[i].par_id << ", "
236                    << bookmarks[i].par_pos << ", "
237                    << bookmarks[i].filename << '\n';
238         }
239 }
240
241
242 void BookmarksSection::save(std::string const & fname, int par_id, pos_type par_pos, bool persistent)
243 {
244         if (persistent) {
245                 bookmarks.push_front(Bookmark(fname, par_id, par_pos));
246                 if (bookmarks.size() > max_bookmarks)
247                         bookmarks.pop_back();
248                 }
249         else
250                 temp_bookmark = Bookmark(fname, par_id, par_pos);
251 }
252
253
254 bool BookmarksSection::isValid(unsigned int i) const
255 {
256         // i == 0, or in the queue
257         return i <= bookmarks.size();
258 }
259
260
261 BookmarksSection::Bookmark const & BookmarksSection::bookmark(unsigned int i) const
262 {
263         if (i == 0)
264                 return temp_bookmark;
265         else
266                 return bookmarks[i-1];
267 }
268
269
270 void SessionInfoSection::read(istream & is)
271 {
272         string tmp;
273         do {
274                 char c = is.peek();
275                 if (c == '[')
276                         break;
277                 getline(is, tmp);
278
279                 // Read session info, saved as key/value pairs
280                 // would better yell if pos returns npos
281                 string::size_type pos = tmp.find_first_of(" = ");
282                 // silently ignore lines without " = "
283                 if (pos != string::npos) {
284                         string key = tmp.substr(0, pos);
285                         string value = tmp.substr(pos + 3);
286                         sessioninfo[key] = value;
287                 }
288         } while (is.good());
289 }
290
291
292 void SessionInfoSection::write(ostream & os) const
293 {
294         os << '\n' << sec_session << '\n';
295         for (MiscInfo::const_iterator val = sessioninfo.begin();
296                 val != sessioninfo.end(); ++val) {
297                 os << val->first << " = " << val->second << '\n';
298         }
299 }
300
301
302 void SessionInfoSection::save(string const & key, string const & value)
303 {
304         sessioninfo[key] = value;
305 }
306
307
308 string const SessionInfoSection::load(string const & key, bool release)
309 {
310         MiscInfo::const_iterator pos = sessioninfo.find(key);
311         string value;
312         if (pos != sessioninfo.end())
313                 value = pos->second;
314         if (release)
315                 sessioninfo.erase(key);
316         return value;
317 }
318
319
320 Session::Session(unsigned int num) :
321         last_files(num)
322 {
323         // locate the session file
324         // note that the session file name 'session' is hard-coded
325         session_file = addName(package().user_support(), "session");
326         //
327         readFile();
328 }
329
330
331 void Session::readFile()
332 {
333         // we will not complain if we can't find session_file nor will
334         // we issue a warning. (Lgb)
335         ifstream is(session_file.c_str());
336         string tmp;
337
338         while (getline(is, tmp)) {
339                 // Ignore comments, empty line or line stats with ' '
340                 if (tmp == "" || tmp[0] == '#' || tmp[0] == ' ')
341                         continue;
342
343                 // Determine section id
344                 if (tmp == sec_lastfiles)
345                         lastFiles().read(is);
346                 else if (tmp == sec_lastopened)
347                         lastOpened().read(is);
348                 else if (tmp == sec_lastfilepos)
349                         lastFilePos().read(is);
350                 else if (tmp == sec_bookmarks)
351                         bookmarks().read(is);
352                 else if (tmp == sec_session)
353                         sessionInfo().read(is);
354                 else
355                         lyxerr << "LyX: Warning: unknown Session section: " << tmp << endl;
356         }
357 }
358
359
360 void Session::writeFile() const
361 {
362         ofstream os(session_file.c_str());
363         if (os) {
364                 os << "## Automatically generated lyx session file \n"
365                     << "## Editing this file manually may cause lyx to crash.\n";
366
367                 lastFiles().write(os);
368                 lastOpened().write(os);
369                 lastFilePos().write(os);
370                 bookmarks().write(os);
371                 sessionInfo().write(os);
372         } else
373                 lyxerr << "LyX: Warning: unable to save Session: "
374                        << session_file << endl;
375 }
376
377 }