svcore  1.9
Selection.cpp
Go to the documentation of this file.
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