svcore
1.9
|
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ 00002 00003 /* 00004 Sonic Visualiser 00005 An audio file viewer and annotation editor. 00006 Centre for Digital Music, Queen Mary, University of London. 00007 This file copyright 2006 Chris Cannam. 00008 00009 This program is free software; you can redistribute it and/or 00010 modify it under the terms of the GNU General Public License as 00011 published by the Free Software Foundation; either version 2 of the 00012 License, or (at your option) any later version. See the file 00013 COPYING included with this distribution for more information. 00014 */ 00015 00016 #include "Selection.h" 00017 #include <QTextStream> 00018 00019 Selection::Selection() : 00020 m_startFrame(0), 00021 m_endFrame(0) 00022 { 00023 } 00024 00025 Selection::Selection(int startFrame, int endFrame) : 00026 m_startFrame(startFrame), 00027 m_endFrame(endFrame) 00028 { 00029 if (m_startFrame > m_endFrame) { 00030 int tmp = m_endFrame; 00031 m_endFrame = m_startFrame; 00032 m_startFrame = tmp; 00033 } 00034 } 00035 00036 Selection::Selection(const Selection &s) : 00037 m_startFrame(s.m_startFrame), 00038 m_endFrame(s.m_endFrame) 00039 { 00040 } 00041 00042 Selection & 00043 Selection::operator=(const Selection &s) 00044 { 00045 if (this != &s) { 00046 m_startFrame = s.m_startFrame; 00047 m_endFrame = s.m_endFrame; 00048 } 00049 return *this; 00050 } 00051 00052 Selection::~Selection() 00053 { 00054 } 00055 00056 bool 00057 Selection::isEmpty() const 00058 { 00059 return m_startFrame == m_endFrame; 00060 } 00061 00062 int 00063 Selection::getStartFrame() const 00064 { 00065 return m_startFrame; 00066 } 00067 00068 int 00069 Selection::getEndFrame() const 00070 { 00071 return m_endFrame; 00072 } 00073 00074 bool 00075 Selection::contains(int frame) const 00076 { 00077 return (frame >= m_startFrame) && (frame < m_endFrame); 00078 } 00079 00080 bool 00081 Selection::operator<(const Selection &s) const 00082 { 00083 if (isEmpty()) { 00084 if (s.isEmpty()) return false; 00085 else return true; 00086 } else { 00087 if (s.isEmpty()) return false; 00088 else return (m_startFrame < s.m_startFrame); 00089 } 00090 } 00091 00092 bool 00093 Selection::operator==(const Selection &s) const 00094 { 00095 if (isEmpty()) return s.isEmpty(); 00096 00097 return (m_startFrame == s.m_startFrame && 00098 m_endFrame == s.m_endFrame); 00099 } 00100 00101 00102 MultiSelection::MultiSelection() 00103 { 00104 } 00105 00106 MultiSelection::~MultiSelection() 00107 { 00108 } 00109 00110 const MultiSelection::SelectionList & 00111 MultiSelection::getSelections() const 00112 { 00113 return m_selections; 00114 } 00115 00116 void 00117 MultiSelection::setSelection(const Selection &selection) 00118 { 00119 clearSelections(); 00120 addSelection(selection); 00121 } 00122 00123 void 00124 MultiSelection::addSelection(const Selection &selection) 00125 { 00126 m_selections.insert(selection); 00127 00128 // Cope with a sitation where the new selection overlaps one or 00129 // more existing ones. This is a terribly inefficient way to do 00130 // this, but that probably isn't significant in real life. 00131 00132 // It's essential for the correct operation of 00133 // getContainingSelection that the selections do not overlap, so 00134 // this is not just a frill. 00135 00136 for (SelectionList::iterator i = m_selections.begin(); 00137 i != m_selections.end(); ) { 00138 00139 SelectionList::iterator j = i; 00140 if (++j == m_selections.end()) break; 00141 00142 if (i->getEndFrame() >= j->getStartFrame()) { 00143 Selection merged(i->getStartFrame(), 00144 std::max(i->getEndFrame(), j->getEndFrame())); 00145 m_selections.erase(i); 00146 m_selections.erase(j); 00147 m_selections.insert(merged); 00148 i = m_selections.begin(); 00149 } else { 00150 ++i; 00151 } 00152 } 00153 } 00154 00155 void 00156 MultiSelection::removeSelection(const Selection &selection) 00157 { 00159 //where selection is not one of the original selection set but 00160 //simply overlaps one of them (cutting down the original selection 00161 //appropriately) 00162 00163 if (m_selections.find(selection) != m_selections.end()) { 00164 m_selections.erase(selection); 00165 } 00166 } 00167 00168 void 00169 MultiSelection::clearSelections() 00170 { 00171 if (!m_selections.empty()) { 00172 m_selections.clear(); 00173 } 00174 } 00175 00176 void 00177 MultiSelection::getExtents(int &startFrame, int &endFrame) const 00178 { 00179 startFrame = 0; 00180 endFrame = 0; 00181 00182 for (SelectionList::const_iterator i = m_selections.begin(); 00183 i != m_selections.end(); ++i) { 00184 00185 if (i == m_selections.begin() || i->getStartFrame() < startFrame) { 00186 startFrame = i->getStartFrame(); 00187 } 00188 00189 if (i == m_selections.begin() || i->getEndFrame() > endFrame) { 00190 endFrame = i->getEndFrame(); 00191 } 00192 } 00193 } 00194 00195 Selection 00196 MultiSelection::getContainingSelection(int frame, bool defaultToFollowing) const 00197 { 00198 // This scales very badly with the number of selections, but it's 00199 // more efficient for very small numbers of selections than a more 00200 // scalable method, and I think that may be what we need 00201 00202 for (SelectionList::const_iterator i = m_selections.begin(); 00203 i != m_selections.end(); ++i) { 00204 00205 if (i->contains(frame)) return *i; 00206 00207 if (i->getStartFrame() > frame) { 00208 if (defaultToFollowing) return *i; 00209 else return Selection(); 00210 } 00211 } 00212 00213 return Selection(); 00214 } 00215 00216 void 00217 MultiSelection::toXml(QTextStream &stream, QString indent, 00218 QString extraAttributes) const 00219 { 00220 stream << indent << QString("<selections %1>\n").arg(extraAttributes); 00221 for (SelectionList::iterator i = m_selections.begin(); 00222 i != m_selections.end(); ++i) { 00223 stream << indent 00224 << QString(" <selection start=\"%1\" end=\"%2\"/>\n") 00225 .arg(i->getStartFrame()).arg(i->getEndFrame()); 00226 } 00227 stream << indent << "</selections>\n"; 00228 } 00229