1 /*
2 * Copyright (C) 2008-2016 Paul Davis <paul@linuxaudiosystems.com>
3 * Copyright (C) 2009-2012 David Robillard <d@drobilla.net>
4 * Copyright (C) 2011-2013 Sakari Bergen <sakari.bergen@beatwaves.net>
5 * Copyright (C) 2012-2016 Tim Mayberry <mojofunk@gmail.com>
6 * Copyright (C) 2014 Nick Mainsbridge <mainsbridge@gmail.com>
7 * Copyright (C) 2015-2018 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 <string>
25
26 #include <glibmm/miscutils.h>
27 #include <glibmm/fileutils.h>
28
29 #include "pbd/xml++.h"
30 #include "pbd/string_convert.h"
31 #include "pbd/enumwriter.h"
32 #include "pbd/localtime_r.h"
33
34 #include "ardour/libardour_visibility.h"
35 #include "ardour/session.h"
36 #include "ardour/session_directory.h"
37 #include "ardour/export_filename.h"
38 #include "ardour/export_format_specification.h"
39 #include "ardour/export_channel_configuration.h"
40 #include "ardour/export_timespan.h"
41 #include "ardour/utils.h"
42
43 #include "pbd/i18n.h"
44
45 using namespace PBD;
46 using namespace Glib;
47 using std::string;
48
49 namespace ARDOUR
50 {
51
ExportFilename(Session & session)52 ExportFilename::ExportFilename (Session & session) :
53 include_label (false),
54 include_session (false),
55 use_session_snapshot_name (false),
56 include_revision (false),
57 include_channel_config (false),
58 include_format_name (false),
59 include_channel (false),
60 include_timespan (true), // Include timespan name always
61 include_time (false),
62 include_date (false),
63 session (session),
64 revision (1),
65 date_format (D_None),
66 time_format (T_None)
67 {
68 time_t rawtime;
69 std::time (&rawtime);
70 localtime_r (&rawtime, &time_struct);
71
72 folder = session.session_directory().export_path();
73
74 XMLNode * extra_node = session.extra_xml ("ExportFilename");
75 /* Legacy sessions used Session instant.xml for this */
76 if (!extra_node) {
77 session.instant_xml ("ExportFilename");
78 }
79
80 if (extra_node) {
81 set_state (*extra_node);
82 }
83 }
84
85 XMLNode &
get_state()86 ExportFilename::get_state ()
87 {
88 XMLNode * node = new XMLNode ("ExportFilename");
89 XMLNode * child;
90
91 FieldPair dir = analyse_folder();
92 child = node->add_child ("Folder");
93 child->set_property ("relative", dir.first);
94 child->set_property ("path", dir.second);
95
96 add_field (node, "label", include_label, label);
97 add_field (node, "session", include_session);
98 add_field (node, "snapshot", use_session_snapshot_name);
99 add_field (node, "timespan", include_timespan);
100 add_field (node, "revision", include_revision);
101 add_field (node, "time", include_time, enum_2_string (time_format));
102 add_field (node, "date", include_date, enum_2_string (date_format));
103
104 XMLNode * extra_node = new XMLNode ("ExportRevision");
105 extra_node->set_property ("revision", revision);
106 session.add_extra_xml (*extra_node);
107
108 return *node;
109 }
110
111 int
set_state(const XMLNode & node)112 ExportFilename::set_state (const XMLNode & node)
113 {
114 XMLNode * child;
115 FieldPair pair;
116
117 child = node.child ("Folder");
118 if (!child) { return -1; }
119
120 folder = "";
121
122 bool is_relative;
123 if (child->get_property ("relative", is_relative) && is_relative) {
124 folder = session.session_directory ().root_path ();
125 }
126
127 std::string tmp;
128 if (child->get_property ("path", tmp)) {
129 tmp = Glib::build_filename (folder, tmp);
130 if (!Glib::file_test (tmp, Glib::FILE_TEST_EXISTS)) {
131 warning << string_compose (_("Existing export folder for this session (%1) does not exist - using default"), tmp) << endmsg;
132 folder = session.session_directory().export_path();
133 } else {
134 folder = tmp;
135 }
136 }
137
138 if (folder.empty() || !Glib::file_test (folder, FileTest (FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) {
139 folder = session.session_directory().export_path();
140 }
141
142 pair = get_field (node, "label");
143 include_label = pair.first;
144 label = pair.second;
145
146 pair = get_field (node, "session");
147 include_session = pair.first;
148
149 pair = get_field (node, "snapshot");
150 use_session_snapshot_name = pair.first;
151
152 pair = get_field (node, "timespan");
153 include_timespan = pair.first;
154
155 pair = get_field (node, "revision");
156 include_revision = pair.first;
157
158 pair = get_field (node, "time");
159 include_time = pair.first;
160 time_format = (TimeFormat) string_2_enum (pair.second, time_format);
161
162 pair = get_field (node, "date");
163 include_date = pair.first;
164 date_format = (DateFormat) string_2_enum (pair.second, date_format);
165
166 XMLNode * extra_node = session.extra_xml ("ExportRevision");
167 /* Legacy sessions used Session instant.xml for this */
168 if (!extra_node) {
169 extra_node = session.instant_xml ("ExportRevision");
170 }
171
172 if (extra_node) {
173 extra_node->get_property ("revision", revision);
174 }
175
176 return 0;
177 }
178
179 string
get_path(ExportFormatSpecPtr format) const180 ExportFilename::get_path (ExportFormatSpecPtr format) const
181 {
182 string path;
183 bool filename_empty = true;
184 bool with_timespan = include_timespan;
185
186 if (!include_session
187 && !include_label
188 && !include_revision
189 && !include_timespan
190 && !include_channel_config
191 && !include_format_name
192 && !include_channel
193 && !include_date) {
194 with_timespan = true;
195 }
196
197 if (include_session) {
198 path += filename_empty ? "" : "_";
199 if (use_session_snapshot_name) {
200 path += session.snap_name();
201 } else {
202 path += session.name();
203 }
204 filename_empty = false;
205 }
206
207 if (include_label) {
208 path += filename_empty ? "" : "_";
209 path += label;
210 filename_empty = false;
211 }
212
213 if (include_revision) {
214 path += filename_empty ? "" : "_";
215 path += "r";
216 path += to_string (revision);
217 filename_empty = false;
218 }
219
220 if (with_timespan && timespan) {
221 path += filename_empty ? "" : "_";
222 path += timespan->name();
223 filename_empty = false;
224 }
225
226 if (include_channel_config && channel_config) {
227 path += filename_empty ? "" : "_";
228 path += channel_config->name();
229 filename_empty = false;
230 }
231
232 if (include_channel) {
233 path += filename_empty ? "" : "_";
234 path += "channel";
235 path += to_string (channel);
236 filename_empty = false;
237 }
238
239 if (include_date) {
240 path += filename_empty ? "" : "_";
241 path += get_date_format_str (date_format);
242 filename_empty = false;
243 }
244
245 if (include_time) {
246 path += filename_empty ? "" : "_";
247 path += get_time_format_str (time_format);
248 filename_empty = false;
249 }
250
251 if (include_format_name && format) {
252 path += filename_empty ? "" : "_";
253 path += format->name();
254 filename_empty = false;
255 }
256
257 if (path.empty ()) {
258 path = "export";
259 }
260
261 if (format) {
262 path += ".";
263 path += format->extension ();
264 }
265
266 path = legalize_for_universal_path (path);
267
268 return Glib::build_filename (folder, path);
269 }
270
271 string
get_time_format_str(TimeFormat format) const272 ExportFilename::get_time_format_str (TimeFormat format) const
273 {
274 switch ( format ) {
275 case T_None:
276 return _("No Time");
277
278 case T_NoDelim:
279 return get_formatted_time ("%H%M");
280
281 case T_Delim:
282 return get_formatted_time ("%H.%M");
283
284 default:
285 return _("Invalid time format");
286 }
287 }
288
289 string
get_date_format_str(DateFormat format) const290 ExportFilename::get_date_format_str (DateFormat format) const
291 {
292 switch (format) {
293 case D_None:
294 return _("No Date");
295
296 case D_BE:
297 return get_formatted_time ("%Y%m%d");
298
299 case D_ISO:
300 return get_formatted_time ("%Y-%m-%d");
301
302 case D_BEShortY:
303 return get_formatted_time ("%y%m%d");
304
305 case D_ISOShortY:
306 return get_formatted_time ("%y-%m-%d");
307
308 default:
309 return _("Invalid date format");
310 }
311 }
312
313 void
set_time_format(TimeFormat format)314 ExportFilename::set_time_format (TimeFormat format)
315 {
316 time_format = format;
317
318 if (format == T_None) {
319 include_time = false;
320 } else {
321 include_time = true;
322 }
323 }
324
325 void
set_date_format(DateFormat format)326 ExportFilename::set_date_format (DateFormat format)
327 {
328 date_format = format;
329
330 if (format == D_None) {
331 include_date = false;
332 } else {
333 include_date = true;
334 }
335 }
336
337 void
set_label(string value)338 ExportFilename::set_label (string value)
339 {
340 label = value;
341 include_label = !value.compare ("");
342 }
343
344 bool
set_folder(string path)345 ExportFilename::set_folder (string path)
346 {
347 // TODO check folder existence
348 folder = path;
349 return true;
350 }
351
352 string
get_formatted_time(string const & format) const353 ExportFilename::get_formatted_time (string const & format) const
354 {
355 char buffer [80];
356 strftime (buffer, 80, format.c_str(), &time_struct);
357
358 string return_value (buffer);
359 return return_value;
360 }
361
362 void
add_field(XMLNode * node,string const & name,bool enabled,string const & value)363 ExportFilename::add_field (XMLNode * node, string const & name, bool enabled, string const & value)
364 {
365 XMLNode * child = node->add_child ("Field");
366
367 if (!child) {
368 std::cerr << "Error adding a field to ExportFilename XML-tree" << std::endl;
369 return;
370 }
371
372 child->set_property ("name", name);
373 child->set_property ("enabled", enabled);
374 if (!value.empty()) {
375 child->set_property ("value", value);
376 }
377 }
378
379 ExportFilename::FieldPair
get_field(XMLNode const & node,string const & name)380 ExportFilename::get_field (XMLNode const & node, string const & name)
381 {
382 FieldPair pair;
383 pair.first = false;
384
385 XMLNodeList children = node.children();
386
387 for (XMLNodeList::iterator it = children.begin(); it != children.end(); ++it) {
388 std::string str;
389 if ((*it)->get_property ("name", str) && name == str) {
390
391 (*it)->get_property ("enabled", pair.first);
392 (*it)->get_property ("value", pair.second);
393
394 return pair;
395 }
396 }
397
398 return pair;
399 }
400
401 ExportFilename::FieldPair
analyse_folder()402 ExportFilename::analyse_folder ()
403 {
404 FieldPair pair;
405
406 string session_dir = session.session_directory().root_path();
407 string::size_type session_dir_len = session_dir.length();
408
409 string folder_beginning = folder.substr (0, session_dir_len);
410
411 if (!folder_beginning.compare (session_dir)) {
412 pair.first = true;
413 // remove the leading slash if needed.
414 pair.second = folder.substr (folder.length() > session_dir_len ? session_dir_len+1 : session_dir_len);
415 } else {
416 pair.first = false;
417 pair.second = folder;
418 }
419
420 return pair;
421 }
422
423 } // namespace ARDOUR
424