get looping to work again
[ardour.git] / libs / ardour / location_importer.cc
1 /*
2  * Copyright (C) 2008-2010 Carl Hetherington <carl@carlh.net>
3  * Copyright (C) 2008 Sakari Bergen <sakari.bergen@beatwaves.net>
4  * Copyright (C) 2009-2010 David Robillard <d@drobilla.net>
5  * Copyright (C) 2009-2017 Paul Davis <paul@linuxaudiosystems.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include "ardour/location_importer.h"
23
24 #include <sstream>
25 #include <stdexcept>
26
27 #include "ardour/session.h"
28 #include "pbd/convert.h"
29 #include "pbd/failed_constructor.h"
30
31 #include "pbd/i18n.h"
32
33 using namespace std;
34 using namespace PBD;
35 using namespace ARDOUR;
36
37 /**** Handler ***/
38 LocationImportHandler::LocationImportHandler (XMLTree const & source, Session & session) :
39   ElementImportHandler (source, session)
40 {
41         XMLNode const *root = source.root();
42         XMLNode const * location_node;
43
44         if (!(location_node = root->child ("Locations"))) {
45                 throw failed_constructor();
46         }
47
48         // Construct importable locations
49         XMLNodeList const & locations = location_node->children();
50         for (XMLNodeList::const_iterator it = locations.begin(); it != locations.end(); ++it) {
51                 try {
52                         elements.push_back (ElementPtr ( new LocationImporter (source, session, *this, **it)));
53                 } catch (failed_constructor const&) {
54                         _dirty = true;
55                 }
56         }
57 }
58
59 string
60 LocationImportHandler::get_info () const
61 {
62         return _("Locations");
63 }
64
65 /*** LocationImporter ***/
66 LocationImporter::LocationImporter (XMLTree const & source, Session & session, LocationImportHandler & handler, XMLNode const & node) :
67   ElementImporter (source, session),
68   handler (handler),
69   xml_location (node),
70   location (0)
71   {
72         // Parse XML
73         bool name_ok = false;
74         XMLPropertyList props = xml_location.properties();
75
76         for (XMLPropertyIterator it = props.begin(); it != props.end(); ++it) {
77                 string prop = (*it)->name();
78                 if (!prop.compare ("id") || !prop.compare ("flags") || !prop.compare ("locked")) {
79                         // All ok
80                 } else if (!prop.compare ("start") || !prop.compare ("end")) {
81                         // Sample rate conversion
82                         (*it)->set_value (rate_convert_samples ((*it)->value()));
83                 } else if (!prop.compare ("name")) {
84                         // rename region if necessary
85                         name = (*it)->value();
86                         name_ok = true;
87                 } else {
88                         std::cerr << string_compose (X_("LocationImporter did not recognise XML-property \"%1\""), prop) << endmsg;
89                 }
90         }
91
92         if (!name_ok) {
93                 error << X_("LocationImporter did not find necessary XML-property \"name\"") << endmsg;
94                 throw failed_constructor();
95         }
96 }
97
98 LocationImporter::~LocationImporter ()
99 {
100         if (!queued()) {
101                 delete location;
102         }
103 }
104
105 string
106 LocationImporter::get_info () const
107 {
108         samplepos_t start, end;
109         Timecode::Time start_time, end_time;
110
111         // Get sample positions
112         std::istringstream iss_start (xml_location.property ("start")->value());
113         iss_start >> start;
114         std::istringstream iss_end (xml_location.property ("end")->value());
115         iss_end >> end;
116
117         // Convert to timecode
118         session.sample_to_timecode (start, start_time, true, false);
119         session.sample_to_timecode (end, end_time, true, false);
120
121         // return info
122         std::ostringstream oss;
123         if (start == end) {
124                 oss << _("Location: ") << timecode_to_string (start_time);
125         } else {
126                 oss << _("Range\nstart: ") << timecode_to_string (start_time) <<
127                   _("\nend: ") << timecode_to_string (end_time);
128         }
129
130         return oss.str();
131 }
132
133 bool
134 LocationImporter::_prepare_move ()
135 {
136         try {
137                 Location const original (session, xml_location);
138                 location = new Location (original); // Updates id
139         } catch (failed_constructor& err) {
140                 throw std::runtime_error (X_("Error in session file!"));
141                 return false;
142         }
143
144         std::pair<bool, string> rename_pair;
145
146         if (location->is_auto_punch()) {
147                 rename_pair = *Rename (_("The location is the Punch range. It will be imported as a normal range.\nYou may rename the imported location:"), name);
148                 if (!rename_pair.first) {
149                         return false;
150                 }
151
152                 name = rename_pair.second;
153                 location->set_auto_punch (false, this);
154                 location->set_is_range_marker (true, this);
155         }
156
157         if (location->is_auto_loop()) {
158                 rename_pair = *Rename (_("The location is a Loop range. It will be imported as a normal range.\nYou may rename the imported location:"), name);
159                 if (!rename_pair.first) { return false; }
160
161                 location->set_auto_loop (false, this);
162                 location->set_is_range_marker (true, this);
163         }
164
165         // duplicate name checking
166         Locations::LocationList const & locations(session.locations()->list());
167         for (Locations::LocationList::const_iterator it = locations.begin(); it != locations.end(); ++it) {
168                 if (!((*it)->name().compare (location->name())) || !handler.check_name (location->name())) {
169                         rename_pair = *Rename (_("A location with that name already exists.\nYou may rename the imported location:"), name);
170                         if (!rename_pair.first) { return false; }
171                         name = rename_pair.second;
172                 }
173         }
174
175         location->set_name (name);
176
177         return true;
178 }
179
180 void
181 LocationImporter::_cancel_move ()
182 {
183         delete location;
184         location = 0;
185 }
186
187 void
188 LocationImporter::_move ()
189 {
190         session.locations()->add (location);
191 }