1 /*
2 * Copyright (C) 2005-2016 Paul Davis <paul@linuxaudiosystems.com>
3 * Copyright (C) 2005 Taybin Rutkin <taybin@taybin.com>
4 * Copyright (C) 2006-2012 Tim Mayberry <mojofunk@gmail.com>
5 * Copyright (C) 2008-2012 David Robillard <d@drobilla.net>
6 * Copyright (C) 2015 John Emmas <john@creativepost.co.uk>
7 * Copyright (C) 2015 Robin Gareus <robin@gareus.org>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 */
23
24 #include <cstring>
25 #include <cerrno>
26 #include <sstream>
27 #include <algorithm>
28
29 #include "pbd/gstdio_compat.h"
30 #include <glibmm/miscutils.h>
31
32 #include "pbd/error.h"
33
34 #include "ardour/rc_configuration.h"
35 #include "ardour/filesystem_paths.h"
36 #include "ardour/recent_sessions.h"
37
38 #include "pbd/i18n.h"
39
40 using namespace std;
41 using namespace ARDOUR;
42 using namespace PBD;
43
44 namespace {
45
46 const char * const recent_file_name = "recent";
47 const char * const recent_templates_file_name = "recent_templates";
48
49 } // anonymous
50
51 int
read_recent_sessions(RecentSessions & rs)52 ARDOUR::read_recent_sessions (RecentSessions& rs)
53 {
54 std::string path = Glib::build_filename (user_config_directory(), recent_file_name);
55 FILE* fin = g_fopen (path.c_str(), "rb");
56
57 if (!fin) {
58 if (errno != ENOENT) {
59 error << string_compose (_("cannot open recent session file %1 (%2)"), path, strerror (errno)) << endmsg;
60 return -1;
61 } else {
62 return 1;
63 }
64 }
65
66 // Read the file into a std::string
67 std::stringstream recent;
68 while (!feof (fin)) {
69 char buf[1024];
70 size_t charsRead = fread (buf, sizeof(char), 1024, fin);
71 if (ferror (fin)) {
72 error << string_compose (_("Error reading recent session file %1 (%2)"), path, strerror (errno)) << endmsg;
73 fclose(fin);
74 return -1;
75 }
76 if (charsRead == 0) {
77 break;
78 }
79 recent.write (buf, charsRead);
80 }
81
82 while (true) {
83
84 pair<string,string> newpair;
85
86 getline(recent, newpair.first);
87
88 if (!recent.good()) {
89 break;
90 }
91
92 getline(recent, newpair.second);
93
94 if (!recent.good()) {
95 break;
96 }
97
98 rs.push_back (newpair);
99 }
100
101 /* display sorting should be done in the GUI, otherwise the
102 * natural order will be broken
103 */
104
105 fclose (fin);
106 return 0;
107 }
108
109 int
read_recent_templates(std::deque<std::string> & rt)110 ARDOUR::read_recent_templates (std::deque<std::string>& rt)
111 {
112 std::string path = Glib::build_filename (user_config_directory(), recent_templates_file_name);
113 FILE* fin = g_fopen (path.c_str(), "rb");
114
115 if (!fin) {
116 if (errno != ENOENT) {
117 error << string_compose (_("Cannot open recent template file %1 (%2)"), path, strerror (errno)) << endmsg;
118 return -1;
119 } else {
120 return 1;
121 }
122 }
123
124 // Copy the file contents into a std::stringstream
125 std::stringstream recent;
126 while (!feof (fin)) {
127 char buf[1024];
128 size_t charsRead = fread (buf, sizeof(char), 1024, fin);
129 if (ferror (fin)) {
130 error << string_compose (_("Error reading recent session file %1 (%2)"), path, strerror (errno)) << endmsg;
131 fclose(fin);
132 return -1;
133 }
134 if (charsRead == 0) {
135 break;
136 }
137 recent.write (buf, charsRead);
138 }
139
140 while (true) {
141
142 std::string session_template_full_name;
143
144 getline(recent, session_template_full_name);
145
146 if (!recent.good()) {
147 break;
148 }
149
150 rt.push_back (session_template_full_name);
151 }
152
153 fclose (fin);
154 return 0;
155 }
156
157 int
write_recent_sessions(RecentSessions & rs)158 ARDOUR::write_recent_sessions (RecentSessions& rs)
159 {
160 FILE* fout = g_fopen (Glib::build_filename (user_config_directory(), recent_file_name).c_str(), "wb");
161
162 if (!fout) {
163 return -1;
164 }
165
166 {
167 stringstream recent;
168
169 for (RecentSessions::iterator i = rs.begin(); i != rs.end(); ++i) {
170 recent << (*i).first << '\n' << (*i).second << endl;
171 }
172
173 string recentString = recent.str();
174 size_t writeSize = recentString.length();
175
176 fwrite(recentString.c_str(), sizeof(char), writeSize, fout);
177
178 if (ferror(fout))
179 {
180 error << string_compose (_("Error writing recent sessions file %1 (%2)"), recent_file_name, strerror (errno)) << endmsg;
181 fclose(fout);
182 return -1;
183 }
184 }
185
186
187
188 fclose (fout);
189
190 return 0;
191 }
192
193 int
write_recent_templates(std::deque<std::string> & rt)194 ARDOUR::write_recent_templates (std::deque<std::string>& rt)
195 {
196 FILE* fout = g_fopen (Glib::build_filename (user_config_directory(), recent_templates_file_name).c_str(), "wb");
197
198 if (!fout) {
199 return -1;
200 }
201
202 stringstream recent;
203
204 for (std::deque<std::string>::const_iterator i = rt.begin(); i != rt.end(); ++i) {
205 recent << (*i) << std::endl;
206 }
207
208 string recentString = recent.str();
209 size_t writeSize = recentString.length();
210
211 fwrite(recentString.c_str(), sizeof(char), writeSize, fout);
212
213 if (ferror(fout))
214 {
215 error << string_compose (_("Error writing saved template file %1 (%2)"), recent_templates_file_name, strerror (errno)) << endmsg;
216 fclose(fout);
217 return -1;
218 }
219
220 fclose (fout);
221
222 return 0;
223 }
224
225 int
store_recent_sessions(string name,string path)226 ARDOUR::store_recent_sessions (string name, string path)
227 {
228 RecentSessions rs;
229
230 if (ARDOUR::read_recent_sessions (rs) < 0) {
231 return -1;
232 }
233
234 pair<string,string> newpair;
235
236 newpair.first = name;
237 newpair.second = path;
238
239 rs.erase(remove(rs.begin(), rs.end(), newpair), rs.end());
240
241 rs.push_front (newpair);
242
243 uint32_t max_recent_sessions = Config->get_max_recent_sessions();
244
245 if (rs.size() > max_recent_sessions) {
246 rs.erase(rs.begin()+max_recent_sessions, rs.end());
247 }
248
249 return ARDOUR::write_recent_sessions (rs);
250 }
251
252 int
store_recent_templates(const std::string & session_template_full_name)253 ARDOUR::store_recent_templates (const std::string& session_template_full_name)
254 {
255 std::deque<std::string> rt;
256
257 if (ARDOUR::read_recent_templates (rt) < 0) {
258 return -1;
259 }
260
261 rt.erase(remove (rt.begin(), rt.end(), session_template_full_name), rt.end());
262
263 rt.push_front (session_template_full_name);
264
265 uint32_t max_recent_templates = Config->get_max_recent_templates ();
266
267 if (rt.size() > max_recent_templates) {
268 rt.erase( rt.begin() + max_recent_templates, rt.end ());
269 }
270
271 return ARDOUR::write_recent_templates (rt);
272 }
273
274 int
remove_recent_sessions(const string & path)275 ARDOUR::remove_recent_sessions (const string& path)
276 {
277 RecentSessions rs;
278 bool write = false;
279
280 if (ARDOUR::read_recent_sessions (rs) < 0) {
281 return -1;
282 }
283
284 for (RecentSessions::iterator i = rs.begin(); i != rs.end(); ++i) {
285 if (i->second == path) {
286 rs.erase (i);
287 write = true;
288 break;
289 }
290 }
291
292 if (write) {
293 return ARDOUR::write_recent_sessions (rs);
294 } else {
295 return 1;
296 }
297 }
298